Commit 2e3b3c42f06ff6801b3d7839757bbdb231619083
Committed by
Laurent Pinchart
1 parent
b9d4745005
Exists in
master
and in
20 other branches
DRM: Add DRM KMS/FB CMA helper
This patchset introduces a set of helper function for implementing the KMS framebuffer layer for drivers which use the DRM GEM CMA helper function. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> Tested-by: Sascha Hauer <s.hauer@pengutronix.de> Acked-by: Sascha Hauer <s.hauer@pengutronix.de> [Make DRM_KMS_CMA_HELPER a boolean Kconfig option] Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Showing 4 changed files with 443 additions and 0 deletions Side-by-side Diff
drivers/gpu/drm/Kconfig
... | ... | @@ -60,6 +60,15 @@ |
60 | 60 | help |
61 | 61 | Choose this if you need the GEM CMA helper functions |
62 | 62 | |
63 | +config DRM_KMS_CMA_HELPER | |
64 | + bool | |
65 | + select DRM_GEM_CMA_HELPER | |
66 | + select FB_SYS_FILLRECT | |
67 | + select FB_SYS_COPYAREA | |
68 | + select FB_SYS_IMAGEBLIT | |
69 | + help | |
70 | + Choose this if you need the KMS CMA helper functions | |
71 | + | |
63 | 72 | config DRM_TDFX |
64 | 73 | tristate "3dfx Banshee/Voodoo3+" |
65 | 74 | depends on DRM && PCI |
drivers/gpu/drm/Makefile
... | ... | @@ -21,6 +21,7 @@ |
21 | 21 | |
22 | 22 | drm_kms_helper-y := drm_fb_helper.o drm_crtc_helper.o drm_dp_i2c_helper.o |
23 | 23 | drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o |
24 | +drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o | |
24 | 25 | |
25 | 26 | obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o |
26 | 27 |
drivers/gpu/drm/drm_fb_cma_helper.c
1 | +/* | |
2 | + * drm kms/fb cma (contiguous memory allocator) helper functions | |
3 | + * | |
4 | + * Copyright (C) 2012 Analog Device Inc. | |
5 | + * Author: Lars-Peter Clausen <lars@metafoo.de> | |
6 | + * | |
7 | + * Based on udl_fbdev.c | |
8 | + * Copyright (C) 2012 Red Hat | |
9 | + * | |
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. | |
18 | + */ | |
19 | + | |
20 | +#include <drm/drmP.h> | |
21 | +#include <drm/drm_crtc.h> | |
22 | +#include <drm/drm_fb_helper.h> | |
23 | +#include <drm/drm_crtc_helper.h> | |
24 | +#include <drm/drm_gem_cma_helper.h> | |
25 | +#include <drm/drm_fb_cma_helper.h> | |
26 | +#include <linux/module.h> | |
27 | + | |
28 | +struct drm_fb_cma { | |
29 | + struct drm_framebuffer fb; | |
30 | + struct drm_gem_cma_object *obj[4]; | |
31 | +}; | |
32 | + | |
33 | +struct drm_fbdev_cma { | |
34 | + struct drm_fb_helper fb_helper; | |
35 | + struct drm_fb_cma *fb; | |
36 | +}; | |
37 | + | |
38 | +static inline struct drm_fbdev_cma *to_fbdev_cma(struct drm_fb_helper *helper) | |
39 | +{ | |
40 | + return container_of(helper, struct drm_fbdev_cma, fb_helper); | |
41 | +} | |
42 | + | |
43 | +static inline struct drm_fb_cma *to_fb_cma(struct drm_framebuffer *fb) | |
44 | +{ | |
45 | + return container_of(fb, struct drm_fb_cma, fb); | |
46 | +} | |
47 | + | |
48 | +static void drm_fb_cma_destroy(struct drm_framebuffer *fb) | |
49 | +{ | |
50 | + struct drm_fb_cma *fb_cma = to_fb_cma(fb); | |
51 | + int i; | |
52 | + | |
53 | + for (i = 0; i < 4; i++) { | |
54 | + if (fb_cma->obj[i]) | |
55 | + drm_gem_object_unreference_unlocked(&fb_cma->obj[i]->base); | |
56 | + } | |
57 | + | |
58 | + drm_framebuffer_cleanup(fb); | |
59 | + kfree(fb_cma); | |
60 | +} | |
61 | + | |
62 | +static int drm_fb_cma_create_handle(struct drm_framebuffer *fb, | |
63 | + struct drm_file *file_priv, unsigned int *handle) | |
64 | +{ | |
65 | + struct drm_fb_cma *fb_cma = to_fb_cma(fb); | |
66 | + | |
67 | + return drm_gem_handle_create(file_priv, | |
68 | + &fb_cma->obj[0]->base, handle); | |
69 | +} | |
70 | + | |
71 | +static struct drm_framebuffer_funcs drm_fb_cma_funcs = { | |
72 | + .destroy = drm_fb_cma_destroy, | |
73 | + .create_handle = drm_fb_cma_create_handle, | |
74 | +}; | |
75 | + | |
76 | +static struct drm_fb_cma *drm_fb_cma_alloc(struct drm_device *dev, | |
77 | + struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_cma_object **obj, | |
78 | + unsigned int num_planes) | |
79 | +{ | |
80 | + struct drm_fb_cma *fb_cma; | |
81 | + int ret; | |
82 | + int i; | |
83 | + | |
84 | + fb_cma = kzalloc(sizeof(*fb_cma), GFP_KERNEL); | |
85 | + if (!fb_cma) | |
86 | + return ERR_PTR(-ENOMEM); | |
87 | + | |
88 | + ret = drm_framebuffer_init(dev, &fb_cma->fb, &drm_fb_cma_funcs); | |
89 | + if (ret) { | |
90 | + dev_err(dev->dev, "Failed to initalize framebuffer: %d\n", ret); | |
91 | + kfree(fb_cma); | |
92 | + return ERR_PTR(ret); | |
93 | + } | |
94 | + | |
95 | + drm_helper_mode_fill_fb_struct(&fb_cma->fb, mode_cmd); | |
96 | + | |
97 | + for (i = 0; i < num_planes; i++) | |
98 | + fb_cma->obj[i] = obj[i]; | |
99 | + | |
100 | + return fb_cma; | |
101 | +} | |
102 | + | |
103 | +/** | |
104 | + * drm_fb_cma_create() - (struct drm_mode_config_funcs *)->fb_create callback function | |
105 | + * | |
106 | + * If your hardware has special alignment or pitch requirements these should be | |
107 | + * checked before calling this function. | |
108 | + */ | |
109 | +struct drm_framebuffer *drm_fb_cma_create(struct drm_device *dev, | |
110 | + struct drm_file *file_priv, struct drm_mode_fb_cmd2 *mode_cmd) | |
111 | +{ | |
112 | + struct drm_fb_cma *fb_cma; | |
113 | + struct drm_gem_cma_object *objs[4]; | |
114 | + struct drm_gem_object *obj; | |
115 | + unsigned int hsub; | |
116 | + unsigned int vsub; | |
117 | + int ret; | |
118 | + int i; | |
119 | + | |
120 | + hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format); | |
121 | + vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format); | |
122 | + | |
123 | + for (i = 0; i < drm_format_num_planes(mode_cmd->pixel_format); i++) { | |
124 | + unsigned int width = mode_cmd->width / (i ? hsub : 1); | |
125 | + unsigned int height = mode_cmd->height / (i ? vsub : 1); | |
126 | + unsigned int min_size; | |
127 | + | |
128 | + obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[i]); | |
129 | + if (!obj) { | |
130 | + dev_err(dev->dev, "Failed to lookup GEM object\n"); | |
131 | + ret = -ENXIO; | |
132 | + goto err_gem_object_unreference; | |
133 | + } | |
134 | + | |
135 | + min_size = (height - 1) * mode_cmd->pitches[i] | |
136 | + + width * drm_format_plane_cpp(mode_cmd->pixel_format, i) | |
137 | + + mode_cmd->offsets[i]; | |
138 | + | |
139 | + if (obj->size < min_size) { | |
140 | + drm_gem_object_unreference_unlocked(obj); | |
141 | + ret = -EINVAL; | |
142 | + goto err_gem_object_unreference; | |
143 | + } | |
144 | + objs[i] = to_drm_gem_cma_obj(obj); | |
145 | + } | |
146 | + | |
147 | + fb_cma = drm_fb_cma_alloc(dev, mode_cmd, objs, i); | |
148 | + if (IS_ERR(fb_cma)) { | |
149 | + ret = PTR_ERR(fb_cma); | |
150 | + goto err_gem_object_unreference; | |
151 | + } | |
152 | + | |
153 | + return &fb_cma->fb; | |
154 | + | |
155 | +err_gem_object_unreference: | |
156 | + for (i--; i >= 0; i--) | |
157 | + drm_gem_object_unreference_unlocked(&objs[i]->base); | |
158 | + return ERR_PTR(ret); | |
159 | +} | |
160 | +EXPORT_SYMBOL_GPL(drm_fb_cma_create); | |
161 | + | |
162 | +/** | |
163 | + * drm_fb_cma_get_gem_obj() - Get CMA GEM object for framebuffer | |
164 | + * @fb: The framebuffer | |
165 | + * @plane: Which plane | |
166 | + * | |
167 | + * Return the CMA GEM object for given framebuffer. | |
168 | + * | |
169 | + * This function will usually be called from the CRTC callback functions. | |
170 | + */ | |
171 | +struct drm_gem_cma_object *drm_fb_cma_get_gem_obj(struct drm_framebuffer *fb, | |
172 | + unsigned int plane) | |
173 | +{ | |
174 | + struct drm_fb_cma *fb_cma = to_fb_cma(fb); | |
175 | + | |
176 | + if (plane >= 4) | |
177 | + return NULL; | |
178 | + | |
179 | + return fb_cma->obj[plane]; | |
180 | +} | |
181 | +EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_obj); | |
182 | + | |
183 | +static struct fb_ops drm_fbdev_cma_ops = { | |
184 | + .owner = THIS_MODULE, | |
185 | + .fb_fillrect = sys_fillrect, | |
186 | + .fb_copyarea = sys_copyarea, | |
187 | + .fb_imageblit = sys_imageblit, | |
188 | + .fb_check_var = drm_fb_helper_check_var, | |
189 | + .fb_set_par = drm_fb_helper_set_par, | |
190 | + .fb_blank = drm_fb_helper_blank, | |
191 | + .fb_pan_display = drm_fb_helper_pan_display, | |
192 | + .fb_setcmap = drm_fb_helper_setcmap, | |
193 | +}; | |
194 | + | |
195 | +static int drm_fbdev_cma_create(struct drm_fb_helper *helper, | |
196 | + struct drm_fb_helper_surface_size *sizes) | |
197 | +{ | |
198 | + struct drm_fbdev_cma *fbdev_cma = to_fbdev_cma(helper); | |
199 | + struct drm_mode_fb_cmd2 mode_cmd = { 0 }; | |
200 | + struct drm_device *dev = helper->dev; | |
201 | + struct drm_gem_cma_object *obj; | |
202 | + struct drm_framebuffer *fb; | |
203 | + unsigned int bytes_per_pixel; | |
204 | + unsigned long offset; | |
205 | + struct fb_info *fbi; | |
206 | + size_t size; | |
207 | + int ret; | |
208 | + | |
209 | + DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d\n", | |
210 | + sizes->surface_width, sizes->surface_height, | |
211 | + sizes->surface_bpp); | |
212 | + | |
213 | + bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8); | |
214 | + | |
215 | + mode_cmd.width = sizes->surface_width; | |
216 | + mode_cmd.height = sizes->surface_height; | |
217 | + mode_cmd.pitches[0] = sizes->surface_width * bytes_per_pixel; | |
218 | + mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, | |
219 | + sizes->surface_depth); | |
220 | + | |
221 | + size = mode_cmd.pitches[0] * mode_cmd.height; | |
222 | + obj = drm_gem_cma_create(dev, size); | |
223 | + if (!obj) | |
224 | + return -ENOMEM; | |
225 | + | |
226 | + fbi = framebuffer_alloc(0, dev->dev); | |
227 | + if (!fbi) { | |
228 | + dev_err(dev->dev, "Failed to allocate framebuffer info.\n"); | |
229 | + ret = -ENOMEM; | |
230 | + goto err_drm_gem_cma_free_object; | |
231 | + } | |
232 | + | |
233 | + fbdev_cma->fb = drm_fb_cma_alloc(dev, &mode_cmd, &obj, 1); | |
234 | + if (IS_ERR(fbdev_cma->fb)) { | |
235 | + dev_err(dev->dev, "Failed to allocate DRM framebuffer.\n"); | |
236 | + ret = PTR_ERR(fbdev_cma->fb); | |
237 | + goto err_framebuffer_release; | |
238 | + } | |
239 | + | |
240 | + fb = &fbdev_cma->fb->fb; | |
241 | + helper->fb = fb; | |
242 | + helper->fbdev = fbi; | |
243 | + | |
244 | + fbi->par = helper; | |
245 | + fbi->flags = FBINFO_FLAG_DEFAULT; | |
246 | + fbi->fbops = &drm_fbdev_cma_ops; | |
247 | + | |
248 | + ret = fb_alloc_cmap(&fbi->cmap, 256, 0); | |
249 | + if (ret) { | |
250 | + dev_err(dev->dev, "Failed to allocate color map.\n"); | |
251 | + goto err_drm_fb_cma_destroy; | |
252 | + } | |
253 | + | |
254 | + drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth); | |
255 | + drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height); | |
256 | + | |
257 | + offset = fbi->var.xoffset * bytes_per_pixel; | |
258 | + offset += fbi->var.yoffset * fb->pitches[0]; | |
259 | + | |
260 | + dev->mode_config.fb_base = (resource_size_t)obj->paddr; | |
261 | + fbi->screen_base = obj->vaddr + offset; | |
262 | + fbi->fix.smem_start = (unsigned long)(obj->paddr + offset); | |
263 | + fbi->screen_size = size; | |
264 | + fbi->fix.smem_len = size; | |
265 | + | |
266 | + return 0; | |
267 | + | |
268 | +err_drm_fb_cma_destroy: | |
269 | + drm_fb_cma_destroy(fb); | |
270 | +err_framebuffer_release: | |
271 | + framebuffer_release(fbi); | |
272 | +err_drm_gem_cma_free_object: | |
273 | + drm_gem_cma_free_object(&obj->base); | |
274 | + return ret; | |
275 | +} | |
276 | + | |
277 | +static int drm_fbdev_cma_probe(struct drm_fb_helper *helper, | |
278 | + struct drm_fb_helper_surface_size *sizes) | |
279 | +{ | |
280 | + int ret = 0; | |
281 | + | |
282 | + if (!helper->fb) { | |
283 | + ret = drm_fbdev_cma_create(helper, sizes); | |
284 | + if (ret < 0) | |
285 | + return ret; | |
286 | + ret = 1; | |
287 | + } | |
288 | + | |
289 | + return ret; | |
290 | +} | |
291 | + | |
292 | +static struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = { | |
293 | + .fb_probe = drm_fbdev_cma_probe, | |
294 | +}; | |
295 | + | |
296 | +/** | |
297 | + * drm_fbdev_cma_init() - Allocate and initializes a drm_fbdev_cma struct | |
298 | + * @dev: DRM device | |
299 | + * @preferred_bpp: Preferred bits per pixel for the device | |
300 | + * @num_crtc: Number of CRTCs | |
301 | + * @max_conn_count: Maximum number of connectors | |
302 | + * | |
303 | + * Returns a newly allocated drm_fbdev_cma struct or a ERR_PTR. | |
304 | + */ | |
305 | +struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev, | |
306 | + unsigned int preferred_bpp, unsigned int num_crtc, | |
307 | + unsigned int max_conn_count) | |
308 | +{ | |
309 | + struct drm_fbdev_cma *fbdev_cma; | |
310 | + struct drm_fb_helper *helper; | |
311 | + int ret; | |
312 | + | |
313 | + fbdev_cma = kzalloc(sizeof(*fbdev_cma), GFP_KERNEL); | |
314 | + if (!fbdev_cma) { | |
315 | + dev_err(dev->dev, "Failed to allocate drm fbdev.\n"); | |
316 | + return ERR_PTR(-ENOMEM); | |
317 | + } | |
318 | + | |
319 | + fbdev_cma->fb_helper.funcs = &drm_fb_cma_helper_funcs; | |
320 | + helper = &fbdev_cma->fb_helper; | |
321 | + | |
322 | + ret = drm_fb_helper_init(dev, helper, num_crtc, max_conn_count); | |
323 | + if (ret < 0) { | |
324 | + dev_err(dev->dev, "Failed to initialize drm fb helper.\n"); | |
325 | + goto err_free; | |
326 | + } | |
327 | + | |
328 | + ret = drm_fb_helper_single_add_all_connectors(helper); | |
329 | + if (ret < 0) { | |
330 | + dev_err(dev->dev, "Failed to add connectors.\n"); | |
331 | + goto err_drm_fb_helper_fini; | |
332 | + | |
333 | + } | |
334 | + | |
335 | + ret = drm_fb_helper_initial_config(helper, preferred_bpp); | |
336 | + if (ret < 0) { | |
337 | + dev_err(dev->dev, "Failed to set inital hw configuration.\n"); | |
338 | + goto err_drm_fb_helper_fini; | |
339 | + } | |
340 | + | |
341 | + return fbdev_cma; | |
342 | + | |
343 | +err_drm_fb_helper_fini: | |
344 | + drm_fb_helper_fini(helper); | |
345 | +err_free: | |
346 | + kfree(fbdev_cma); | |
347 | + | |
348 | + return ERR_PTR(ret); | |
349 | +} | |
350 | +EXPORT_SYMBOL_GPL(drm_fbdev_cma_init); | |
351 | + | |
352 | +/** | |
353 | + * drm_fbdev_cma_fini() - Free drm_fbdev_cma struct | |
354 | + * @fbdev_cma: The drm_fbdev_cma struct | |
355 | + */ | |
356 | +void drm_fbdev_cma_fini(struct drm_fbdev_cma *fbdev_cma) | |
357 | +{ | |
358 | + if (fbdev_cma->fb_helper.fbdev) { | |
359 | + struct fb_info *info; | |
360 | + int ret; | |
361 | + | |
362 | + info = fbdev_cma->fb_helper.fbdev; | |
363 | + ret = unregister_framebuffer(info); | |
364 | + if (ret < 0) | |
365 | + DRM_DEBUG_KMS("failed unregister_framebuffer()\n"); | |
366 | + | |
367 | + if (info->cmap.len) | |
368 | + fb_dealloc_cmap(&info->cmap); | |
369 | + | |
370 | + framebuffer_release(info); | |
371 | + } | |
372 | + | |
373 | + if (fbdev_cma->fb) | |
374 | + drm_fb_cma_destroy(&fbdev_cma->fb->fb); | |
375 | + | |
376 | + drm_fb_helper_fini(&fbdev_cma->fb_helper); | |
377 | + kfree(fbdev_cma); | |
378 | +} | |
379 | +EXPORT_SYMBOL_GPL(drm_fbdev_cma_fini); | |
380 | + | |
381 | +/** | |
382 | + * drm_fbdev_cma_restore_mode() - Restores initial framebuffer mode | |
383 | + * @fbdev_cma: The drm_fbdev_cma struct, may be NULL | |
384 | + * | |
385 | + * This function is usually called from the DRM drivers lastclose callback. | |
386 | + */ | |
387 | +void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma *fbdev_cma) | |
388 | +{ | |
389 | + if (fbdev_cma) | |
390 | + drm_fb_helper_restore_fbdev_mode(&fbdev_cma->fb_helper); | |
391 | +} | |
392 | +EXPORT_SYMBOL_GPL(drm_fbdev_cma_restore_mode); | |
393 | + | |
394 | +/** | |
395 | + * drm_fbdev_cma_hotplug_event() - Poll for hotpulug events | |
396 | + * @fbdev_cma: The drm_fbdev_cma struct, may be NULL | |
397 | + * | |
398 | + * This function is usually called from the DRM drivers output_poll_changed | |
399 | + * callback. | |
400 | + */ | |
401 | +void drm_fbdev_cma_hotplug_event(struct drm_fbdev_cma *fbdev_cma) | |
402 | +{ | |
403 | + if (fbdev_cma) | |
404 | + drm_fb_helper_hotplug_event(&fbdev_cma->fb_helper); | |
405 | +} | |
406 | +EXPORT_SYMBOL_GPL(drm_fbdev_cma_hotplug_event); |
include/drm/drm_fb_cma_helper.h
1 | +#ifndef __DRM_FB_CMA_HELPER_H__ | |
2 | +#define __DRM_FB_CMA_HELPER_H__ | |
3 | + | |
4 | +struct drm_fbdev_cma; | |
5 | +struct drm_gem_cma_object; | |
6 | + | |
7 | +struct drm_framebuffer; | |
8 | +struct drm_device; | |
9 | +struct drm_file; | |
10 | +struct drm_mode_fb_cmd2; | |
11 | + | |
12 | +struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev, | |
13 | + unsigned int preferred_bpp, unsigned int num_crtc, | |
14 | + unsigned int max_conn_count); | |
15 | +void drm_fbdev_cma_fini(struct drm_fbdev_cma *fbdev_cma); | |
16 | + | |
17 | +void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma *fbdev_cma); | |
18 | +void drm_fbdev_cma_hotplug_event(struct drm_fbdev_cma *fbdev_cma); | |
19 | + | |
20 | +struct drm_framebuffer *drm_fb_cma_create(struct drm_device *dev, | |
21 | + struct drm_file *file_priv, struct drm_mode_fb_cmd2 *mode_cmd); | |
22 | + | |
23 | +struct drm_gem_cma_object *drm_fb_cma_get_gem_obj(struct drm_framebuffer *fb, | |
24 | + unsigned int plane); | |
25 | + | |
26 | +#endif |