xref: /openbmc/linux/drivers/gpu/drm/tegra/fb.c (revision 4fc4dca8)
1 /*
2  * Copyright (C) 2012-2013 Avionic Design GmbH
3  * Copyright (C) 2012 NVIDIA CORPORATION.  All rights reserved.
4  *
5  * Based on the KMS/FB CMA helpers
6  *   Copyright (C) 2012 Analog Device Inc.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as
10  * published by the Free Software Foundation.
11  */
12 
13 #include <linux/console.h>
14 
15 #include "drm.h"
16 #include "gem.h"
17 #include <drm/drm_gem_framebuffer_helper.h>
18 #include <drm/drm_modeset_helper.h>
19 
20 #ifdef CONFIG_DRM_FBDEV_EMULATION
21 static inline struct tegra_fbdev *to_tegra_fbdev(struct drm_fb_helper *helper)
22 {
23 	return container_of(helper, struct tegra_fbdev, base);
24 }
25 #endif
26 
27 struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer,
28 				    unsigned int index)
29 {
30 	return to_tegra_bo(drm_gem_fb_get_obj(framebuffer, index));
31 }
32 
33 bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer)
34 {
35 	struct tegra_bo *bo = tegra_fb_get_plane(framebuffer, 0);
36 
37 	if (bo->flags & TEGRA_BO_BOTTOM_UP)
38 		return true;
39 
40 	return false;
41 }
42 
43 int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer,
44 			struct tegra_bo_tiling *tiling)
45 {
46 	uint64_t modifier = framebuffer->modifier;
47 
48 	switch (modifier) {
49 	case DRM_FORMAT_MOD_LINEAR:
50 		tiling->mode = TEGRA_BO_TILING_MODE_PITCH;
51 		tiling->value = 0;
52 		break;
53 
54 	case DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED:
55 		tiling->mode = TEGRA_BO_TILING_MODE_TILED;
56 		tiling->value = 0;
57 		break;
58 
59 	case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0):
60 		tiling->mode = TEGRA_BO_TILING_MODE_BLOCK;
61 		tiling->value = 0;
62 		break;
63 
64 	case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1):
65 		tiling->mode = TEGRA_BO_TILING_MODE_BLOCK;
66 		tiling->value = 1;
67 		break;
68 
69 	case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2):
70 		tiling->mode = TEGRA_BO_TILING_MODE_BLOCK;
71 		tiling->value = 2;
72 		break;
73 
74 	case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3):
75 		tiling->mode = TEGRA_BO_TILING_MODE_BLOCK;
76 		tiling->value = 3;
77 		break;
78 
79 	case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4):
80 		tiling->mode = TEGRA_BO_TILING_MODE_BLOCK;
81 		tiling->value = 4;
82 		break;
83 
84 	case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5):
85 		tiling->mode = TEGRA_BO_TILING_MODE_BLOCK;
86 		tiling->value = 5;
87 		break;
88 
89 	default:
90 		return -EINVAL;
91 	}
92 
93 	return 0;
94 }
95 
96 static const struct drm_framebuffer_funcs tegra_fb_funcs = {
97 	.destroy = drm_gem_fb_destroy,
98 	.create_handle = drm_gem_fb_create_handle,
99 };
100 
101 static struct drm_framebuffer *tegra_fb_alloc(struct drm_device *drm,
102 					      const struct drm_mode_fb_cmd2 *mode_cmd,
103 					      struct tegra_bo **planes,
104 					      unsigned int num_planes)
105 {
106 	struct drm_framebuffer *fb;
107 	unsigned int i;
108 	int err;
109 
110 	fb = kzalloc(sizeof(*fb), GFP_KERNEL);
111 	if (!fb)
112 		return ERR_PTR(-ENOMEM);
113 
114 	drm_helper_mode_fill_fb_struct(drm, fb, mode_cmd);
115 
116 	for (i = 0; i < fb->format->num_planes; i++)
117 		fb->obj[i] = &planes[i]->gem;
118 
119 	err = drm_framebuffer_init(drm, fb, &tegra_fb_funcs);
120 	if (err < 0) {
121 		dev_err(drm->dev, "failed to initialize framebuffer: %d\n",
122 			err);
123 		kfree(fb);
124 		return ERR_PTR(err);
125 	}
126 
127 	return fb;
128 }
129 
130 struct drm_framebuffer *tegra_fb_create(struct drm_device *drm,
131 					struct drm_file *file,
132 					const struct drm_mode_fb_cmd2 *cmd)
133 {
134 	const struct drm_format_info *info = drm_get_format_info(drm, cmd);
135 	struct tegra_bo *planes[4];
136 	struct drm_gem_object *gem;
137 	struct drm_framebuffer *fb;
138 	unsigned int i;
139 	int err;
140 
141 	for (i = 0; i < info->num_planes; i++) {
142 		unsigned int width = cmd->width / (i ? info->hsub : 1);
143 		unsigned int height = cmd->height / (i ? info->vsub : 1);
144 		unsigned int size, bpp;
145 
146 		gem = drm_gem_object_lookup(file, cmd->handles[i]);
147 		if (!gem) {
148 			err = -ENXIO;
149 			goto unreference;
150 		}
151 
152 		bpp = info->cpp[i];
153 
154 		size = (height - 1) * cmd->pitches[i] +
155 		       width * bpp + cmd->offsets[i];
156 
157 		if (gem->size < size) {
158 			err = -EINVAL;
159 			goto unreference;
160 		}
161 
162 		planes[i] = to_tegra_bo(gem);
163 	}
164 
165 	fb = tegra_fb_alloc(drm, cmd, planes, i);
166 	if (IS_ERR(fb)) {
167 		err = PTR_ERR(fb);
168 		goto unreference;
169 	}
170 
171 	return fb;
172 
173 unreference:
174 	while (i--)
175 		drm_gem_object_put_unlocked(&planes[i]->gem);
176 
177 	return ERR_PTR(err);
178 }
179 
180 #ifdef CONFIG_DRM_FBDEV_EMULATION
181 static int tegra_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
182 {
183 	struct drm_fb_helper *helper = info->par;
184 	struct tegra_bo *bo;
185 	int err;
186 
187 	bo = tegra_fb_get_plane(helper->fb, 0);
188 
189 	err = drm_gem_mmap_obj(&bo->gem, bo->gem.size, vma);
190 	if (err < 0)
191 		return err;
192 
193 	return __tegra_gem_mmap(&bo->gem, vma);
194 }
195 
196 static struct fb_ops tegra_fb_ops = {
197 	.owner = THIS_MODULE,
198 	DRM_FB_HELPER_DEFAULT_OPS,
199 	.fb_fillrect = drm_fb_helper_sys_fillrect,
200 	.fb_copyarea = drm_fb_helper_sys_copyarea,
201 	.fb_imageblit = drm_fb_helper_sys_imageblit,
202 	.fb_mmap = tegra_fb_mmap,
203 };
204 
205 static int tegra_fbdev_probe(struct drm_fb_helper *helper,
206 			     struct drm_fb_helper_surface_size *sizes)
207 {
208 	struct tegra_fbdev *fbdev = to_tegra_fbdev(helper);
209 	struct tegra_drm *tegra = helper->dev->dev_private;
210 	struct drm_device *drm = helper->dev;
211 	struct drm_mode_fb_cmd2 cmd = { 0 };
212 	unsigned int bytes_per_pixel;
213 	struct drm_framebuffer *fb;
214 	unsigned long offset;
215 	struct fb_info *info;
216 	struct tegra_bo *bo;
217 	size_t size;
218 	int err;
219 
220 	bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8);
221 
222 	cmd.width = sizes->surface_width;
223 	cmd.height = sizes->surface_height;
224 	cmd.pitches[0] = round_up(sizes->surface_width * bytes_per_pixel,
225 				  tegra->pitch_align);
226 
227 	cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
228 						     sizes->surface_depth);
229 
230 	size = cmd.pitches[0] * cmd.height;
231 
232 	bo = tegra_bo_create(drm, size, 0);
233 	if (IS_ERR(bo))
234 		return PTR_ERR(bo);
235 
236 	info = drm_fb_helper_alloc_fbi(helper);
237 	if (IS_ERR(info)) {
238 		dev_err(drm->dev, "failed to allocate framebuffer info\n");
239 		drm_gem_object_put_unlocked(&bo->gem);
240 		return PTR_ERR(info);
241 	}
242 
243 	fbdev->fb = tegra_fb_alloc(drm, &cmd, &bo, 1);
244 	if (IS_ERR(fbdev->fb)) {
245 		err = PTR_ERR(fbdev->fb);
246 		dev_err(drm->dev, "failed to allocate DRM framebuffer: %d\n",
247 			err);
248 		drm_gem_object_put_unlocked(&bo->gem);
249 		return PTR_ERR(fbdev->fb);
250 	}
251 
252 	fb = fbdev->fb;
253 	helper->fb = fb;
254 	helper->fbdev = info;
255 
256 	info->fbops = &tegra_fb_ops;
257 
258 	drm_fb_helper_fill_info(info, helper, sizes);
259 
260 	offset = info->var.xoffset * bytes_per_pixel +
261 		 info->var.yoffset * fb->pitches[0];
262 
263 	if (bo->pages) {
264 		bo->vaddr = vmap(bo->pages, bo->num_pages, VM_MAP,
265 				 pgprot_writecombine(PAGE_KERNEL));
266 		if (!bo->vaddr) {
267 			dev_err(drm->dev, "failed to vmap() framebuffer\n");
268 			err = -ENOMEM;
269 			goto destroy;
270 		}
271 	}
272 
273 	drm->mode_config.fb_base = (resource_size_t)bo->paddr;
274 	info->screen_base = (void __iomem *)bo->vaddr + offset;
275 	info->screen_size = size;
276 	info->fix.smem_start = (unsigned long)(bo->paddr + offset);
277 	info->fix.smem_len = size;
278 
279 	return 0;
280 
281 destroy:
282 	drm_framebuffer_remove(fb);
283 	return err;
284 }
285 
286 static const struct drm_fb_helper_funcs tegra_fb_helper_funcs = {
287 	.fb_probe = tegra_fbdev_probe,
288 };
289 
290 static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm)
291 {
292 	struct tegra_fbdev *fbdev;
293 
294 	fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
295 	if (!fbdev) {
296 		dev_err(drm->dev, "failed to allocate DRM fbdev\n");
297 		return ERR_PTR(-ENOMEM);
298 	}
299 
300 	drm_fb_helper_prepare(drm, &fbdev->base, &tegra_fb_helper_funcs);
301 
302 	return fbdev;
303 }
304 
305 static void tegra_fbdev_free(struct tegra_fbdev *fbdev)
306 {
307 	kfree(fbdev);
308 }
309 
310 static int tegra_fbdev_init(struct tegra_fbdev *fbdev,
311 			    unsigned int preferred_bpp,
312 			    unsigned int num_crtc,
313 			    unsigned int max_connectors)
314 {
315 	struct drm_device *drm = fbdev->base.dev;
316 	int err;
317 
318 	err = drm_fb_helper_init(drm, &fbdev->base, max_connectors);
319 	if (err < 0) {
320 		dev_err(drm->dev, "failed to initialize DRM FB helper: %d\n",
321 			err);
322 		return err;
323 	}
324 
325 	err = drm_fb_helper_single_add_all_connectors(&fbdev->base);
326 	if (err < 0) {
327 		dev_err(drm->dev, "failed to add connectors: %d\n", err);
328 		goto fini;
329 	}
330 
331 	err = drm_fb_helper_initial_config(&fbdev->base, preferred_bpp);
332 	if (err < 0) {
333 		dev_err(drm->dev, "failed to set initial configuration: %d\n",
334 			err);
335 		goto fini;
336 	}
337 
338 	return 0;
339 
340 fini:
341 	drm_fb_helper_fini(&fbdev->base);
342 	return err;
343 }
344 
345 static void tegra_fbdev_exit(struct tegra_fbdev *fbdev)
346 {
347 	drm_fb_helper_unregister_fbi(&fbdev->base);
348 
349 	if (fbdev->fb) {
350 		struct tegra_bo *bo = tegra_fb_get_plane(fbdev->fb, 0);
351 
352 		/* Undo the special mapping we made in fbdev probe. */
353 		if (bo && bo->pages) {
354 			vunmap(bo->vaddr);
355 			bo->vaddr = NULL;
356 		}
357 
358 		drm_framebuffer_remove(fbdev->fb);
359 	}
360 
361 	drm_fb_helper_fini(&fbdev->base);
362 	tegra_fbdev_free(fbdev);
363 }
364 #endif
365 
366 int tegra_drm_fb_prepare(struct drm_device *drm)
367 {
368 #ifdef CONFIG_DRM_FBDEV_EMULATION
369 	struct tegra_drm *tegra = drm->dev_private;
370 
371 	tegra->fbdev = tegra_fbdev_create(drm);
372 	if (IS_ERR(tegra->fbdev))
373 		return PTR_ERR(tegra->fbdev);
374 #endif
375 
376 	return 0;
377 }
378 
379 void tegra_drm_fb_free(struct drm_device *drm)
380 {
381 #ifdef CONFIG_DRM_FBDEV_EMULATION
382 	struct tegra_drm *tegra = drm->dev_private;
383 
384 	tegra_fbdev_free(tegra->fbdev);
385 #endif
386 }
387 
388 int tegra_drm_fb_init(struct drm_device *drm)
389 {
390 #ifdef CONFIG_DRM_FBDEV_EMULATION
391 	struct tegra_drm *tegra = drm->dev_private;
392 	int err;
393 
394 	err = tegra_fbdev_init(tegra->fbdev, 32, drm->mode_config.num_crtc,
395 			       drm->mode_config.num_connector);
396 	if (err < 0)
397 		return err;
398 #endif
399 
400 	return 0;
401 }
402 
403 void tegra_drm_fb_exit(struct drm_device *drm)
404 {
405 #ifdef CONFIG_DRM_FBDEV_EMULATION
406 	struct tegra_drm *tegra = drm->dev_private;
407 
408 	tegra_fbdev_exit(tegra->fbdev);
409 #endif
410 }
411