xref: /openbmc/linux/drivers/gpu/drm/tegra/fb.c (revision c4ee0af3)
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 "drm.h"
14 #include "gem.h"
15 
16 static inline struct tegra_fb *to_tegra_fb(struct drm_framebuffer *fb)
17 {
18 	return container_of(fb, struct tegra_fb, base);
19 }
20 
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 
26 struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer,
27 				    unsigned int index)
28 {
29 	struct tegra_fb *fb = to_tegra_fb(framebuffer);
30 
31 	if (index >= drm_format_num_planes(framebuffer->pixel_format))
32 		return NULL;
33 
34 	return fb->planes[index];
35 }
36 
37 bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer)
38 {
39 	struct tegra_fb *fb = to_tegra_fb(framebuffer);
40 
41 	if (fb->planes[0]->flags & TEGRA_BO_BOTTOM_UP)
42 		return true;
43 
44 	return false;
45 }
46 
47 bool tegra_fb_is_tiled(struct drm_framebuffer *framebuffer)
48 {
49 	struct tegra_fb *fb = to_tegra_fb(framebuffer);
50 
51 	if (fb->planes[0]->flags & TEGRA_BO_TILED)
52 		return true;
53 
54 	return false;
55 }
56 
57 static void tegra_fb_destroy(struct drm_framebuffer *framebuffer)
58 {
59 	struct tegra_fb *fb = to_tegra_fb(framebuffer);
60 	unsigned int i;
61 
62 	for (i = 0; i < fb->num_planes; i++) {
63 		struct tegra_bo *bo = fb->planes[i];
64 
65 		if (bo)
66 			drm_gem_object_unreference_unlocked(&bo->gem);
67 	}
68 
69 	drm_framebuffer_cleanup(framebuffer);
70 	kfree(fb->planes);
71 	kfree(fb);
72 }
73 
74 static int tegra_fb_create_handle(struct drm_framebuffer *framebuffer,
75 				  struct drm_file *file, unsigned int *handle)
76 {
77 	struct tegra_fb *fb = to_tegra_fb(framebuffer);
78 
79 	return drm_gem_handle_create(file, &fb->planes[0]->gem, handle);
80 }
81 
82 static struct drm_framebuffer_funcs tegra_fb_funcs = {
83 	.destroy = tegra_fb_destroy,
84 	.create_handle = tegra_fb_create_handle,
85 };
86 
87 static struct tegra_fb *tegra_fb_alloc(struct drm_device *drm,
88 				       struct drm_mode_fb_cmd2 *mode_cmd,
89 				       struct tegra_bo **planes,
90 				       unsigned int num_planes)
91 {
92 	struct tegra_fb *fb;
93 	unsigned int i;
94 	int err;
95 
96 	fb = kzalloc(sizeof(*fb), GFP_KERNEL);
97 	if (!fb)
98 		return ERR_PTR(-ENOMEM);
99 
100 	fb->planes = kzalloc(num_planes * sizeof(*planes), GFP_KERNEL);
101 	if (!fb->planes)
102 		return ERR_PTR(-ENOMEM);
103 
104 	fb->num_planes = num_planes;
105 
106 	drm_helper_mode_fill_fb_struct(&fb->base, mode_cmd);
107 
108 	for (i = 0; i < fb->num_planes; i++)
109 		fb->planes[i] = planes[i];
110 
111 	err = drm_framebuffer_init(drm, &fb->base, &tegra_fb_funcs);
112 	if (err < 0) {
113 		dev_err(drm->dev, "failed to initialize framebuffer: %d\n",
114 			err);
115 		kfree(fb->planes);
116 		kfree(fb);
117 		return ERR_PTR(err);
118 	}
119 
120 	return fb;
121 }
122 
123 static struct drm_framebuffer *tegra_fb_create(struct drm_device *drm,
124 					       struct drm_file *file,
125 					       struct drm_mode_fb_cmd2 *cmd)
126 {
127 	unsigned int hsub, vsub, i;
128 	struct tegra_bo *planes[4];
129 	struct drm_gem_object *gem;
130 	struct tegra_fb *fb;
131 	int err;
132 
133 	hsub = drm_format_horz_chroma_subsampling(cmd->pixel_format);
134 	vsub = drm_format_vert_chroma_subsampling(cmd->pixel_format);
135 
136 	for (i = 0; i < drm_format_num_planes(cmd->pixel_format); i++) {
137 		unsigned int width = cmd->width / (i ? hsub : 1);
138 		unsigned int height = cmd->height / (i ? vsub : 1);
139 		unsigned int size, bpp;
140 
141 		gem = drm_gem_object_lookup(drm, file, cmd->handles[i]);
142 		if (!gem) {
143 			err = -ENXIO;
144 			goto unreference;
145 		}
146 
147 		bpp = drm_format_plane_cpp(cmd->pixel_format, i);
148 
149 		size = (height - 1) * cmd->pitches[i] +
150 		       width * bpp + cmd->offsets[i];
151 
152 		if (gem->size < size) {
153 			err = -EINVAL;
154 			goto unreference;
155 		}
156 
157 		planes[i] = to_tegra_bo(gem);
158 	}
159 
160 	fb = tegra_fb_alloc(drm, cmd, planes, i);
161 	if (IS_ERR(fb)) {
162 		err = PTR_ERR(fb);
163 		goto unreference;
164 	}
165 
166 	return &fb->base;
167 
168 unreference:
169 	while (i--)
170 		drm_gem_object_unreference_unlocked(&planes[i]->gem);
171 
172 	return ERR_PTR(err);
173 }
174 
175 static struct fb_ops tegra_fb_ops = {
176 	.owner = THIS_MODULE,
177 	.fb_fillrect = sys_fillrect,
178 	.fb_copyarea = sys_copyarea,
179 	.fb_imageblit = sys_imageblit,
180 	.fb_check_var = drm_fb_helper_check_var,
181 	.fb_set_par = drm_fb_helper_set_par,
182 	.fb_blank = drm_fb_helper_blank,
183 	.fb_pan_display = drm_fb_helper_pan_display,
184 	.fb_setcmap = drm_fb_helper_setcmap,
185 };
186 
187 static int tegra_fbdev_probe(struct drm_fb_helper *helper,
188 			     struct drm_fb_helper_surface_size *sizes)
189 {
190 	struct tegra_fbdev *fbdev = to_tegra_fbdev(helper);
191 	struct drm_device *drm = helper->dev;
192 	struct drm_mode_fb_cmd2 cmd = { 0 };
193 	unsigned int bytes_per_pixel;
194 	struct drm_framebuffer *fb;
195 	unsigned long offset;
196 	struct fb_info *info;
197 	struct tegra_bo *bo;
198 	size_t size;
199 	int err;
200 
201 	bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8);
202 
203 	cmd.width = sizes->surface_width;
204 	cmd.height = sizes->surface_height;
205 	cmd.pitches[0] = sizes->surface_width * bytes_per_pixel;
206 	cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
207 						     sizes->surface_depth);
208 
209 	size = cmd.pitches[0] * cmd.height;
210 
211 	bo = tegra_bo_create(drm, size, 0);
212 	if (IS_ERR(bo))
213 		return PTR_ERR(bo);
214 
215 	info = framebuffer_alloc(0, drm->dev);
216 	if (!info) {
217 		dev_err(drm->dev, "failed to allocate framebuffer info\n");
218 		tegra_bo_free_object(&bo->gem);
219 		return -ENOMEM;
220 	}
221 
222 	fbdev->fb = tegra_fb_alloc(drm, &cmd, &bo, 1);
223 	if (IS_ERR(fbdev->fb)) {
224 		dev_err(drm->dev, "failed to allocate DRM framebuffer\n");
225 		err = PTR_ERR(fbdev->fb);
226 		goto release;
227 	}
228 
229 	fb = &fbdev->fb->base;
230 	helper->fb = fb;
231 	helper->fbdev = info;
232 
233 	info->par = helper;
234 	info->flags = FBINFO_FLAG_DEFAULT;
235 	info->fbops = &tegra_fb_ops;
236 
237 	err = fb_alloc_cmap(&info->cmap, 256, 0);
238 	if (err < 0) {
239 		dev_err(drm->dev, "failed to allocate color map: %d\n", err);
240 		goto destroy;
241 	}
242 
243 	drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
244 	drm_fb_helper_fill_var(info, helper, fb->width, fb->height);
245 
246 	offset = info->var.xoffset * bytes_per_pixel +
247 		 info->var.yoffset * fb->pitches[0];
248 
249 	drm->mode_config.fb_base = (resource_size_t)bo->paddr;
250 	info->screen_base = (void __iomem *)bo->vaddr + offset;
251 	info->screen_size = size;
252 	info->fix.smem_start = (unsigned long)(bo->paddr + offset);
253 	info->fix.smem_len = size;
254 
255 	return 0;
256 
257 destroy:
258 	drm_framebuffer_unregister_private(fb);
259 	tegra_fb_destroy(fb);
260 release:
261 	framebuffer_release(info);
262 	return err;
263 }
264 
265 static struct drm_fb_helper_funcs tegra_fb_helper_funcs = {
266 	.fb_probe = tegra_fbdev_probe,
267 };
268 
269 static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm,
270 					      unsigned int preferred_bpp,
271 					      unsigned int num_crtc,
272 					      unsigned int max_connectors)
273 {
274 	struct drm_fb_helper *helper;
275 	struct tegra_fbdev *fbdev;
276 	int err;
277 
278 	fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
279 	if (!fbdev) {
280 		dev_err(drm->dev, "failed to allocate DRM fbdev\n");
281 		return ERR_PTR(-ENOMEM);
282 	}
283 
284 	fbdev->base.funcs = &tegra_fb_helper_funcs;
285 	helper = &fbdev->base;
286 
287 	err = drm_fb_helper_init(drm, &fbdev->base, num_crtc, max_connectors);
288 	if (err < 0) {
289 		dev_err(drm->dev, "failed to initialize DRM FB helper\n");
290 		goto free;
291 	}
292 
293 	err = drm_fb_helper_single_add_all_connectors(&fbdev->base);
294 	if (err < 0) {
295 		dev_err(drm->dev, "failed to add connectors\n");
296 		goto fini;
297 	}
298 
299 	drm_helper_disable_unused_functions(drm);
300 
301 	err = drm_fb_helper_initial_config(&fbdev->base, preferred_bpp);
302 	if (err < 0) {
303 		dev_err(drm->dev, "failed to set initial configuration\n");
304 		goto fini;
305 	}
306 
307 	return fbdev;
308 
309 fini:
310 	drm_fb_helper_fini(&fbdev->base);
311 free:
312 	kfree(fbdev);
313 	return ERR_PTR(err);
314 }
315 
316 static void tegra_fbdev_free(struct tegra_fbdev *fbdev)
317 {
318 	struct fb_info *info = fbdev->base.fbdev;
319 
320 	if (info) {
321 		int err;
322 
323 		err = unregister_framebuffer(info);
324 		if (err < 0)
325 			DRM_DEBUG_KMS("failed to unregister framebuffer\n");
326 
327 		if (info->cmap.len)
328 			fb_dealloc_cmap(&info->cmap);
329 
330 		framebuffer_release(info);
331 	}
332 
333 	if (fbdev->fb) {
334 		drm_framebuffer_unregister_private(&fbdev->fb->base);
335 		tegra_fb_destroy(&fbdev->fb->base);
336 	}
337 
338 	drm_fb_helper_fini(&fbdev->base);
339 	kfree(fbdev);
340 }
341 
342 static void tegra_fb_output_poll_changed(struct drm_device *drm)
343 {
344 	struct tegra_drm *tegra = drm->dev_private;
345 
346 	if (tegra->fbdev)
347 		drm_fb_helper_hotplug_event(&tegra->fbdev->base);
348 }
349 
350 static const struct drm_mode_config_funcs tegra_drm_mode_funcs = {
351 	.fb_create = tegra_fb_create,
352 	.output_poll_changed = tegra_fb_output_poll_changed,
353 };
354 
355 int tegra_drm_fb_init(struct drm_device *drm)
356 {
357 	struct tegra_drm *tegra = drm->dev_private;
358 	struct tegra_fbdev *fbdev;
359 
360 	drm->mode_config.min_width = 0;
361 	drm->mode_config.min_height = 0;
362 
363 	drm->mode_config.max_width = 4096;
364 	drm->mode_config.max_height = 4096;
365 
366 	drm->mode_config.funcs = &tegra_drm_mode_funcs;
367 
368 	fbdev = tegra_fbdev_create(drm, 32, drm->mode_config.num_crtc,
369 				   drm->mode_config.num_connector);
370 	if (IS_ERR(fbdev))
371 		return PTR_ERR(fbdev);
372 
373 	tegra->fbdev = fbdev;
374 
375 	return 0;
376 }
377 
378 void tegra_drm_fb_exit(struct drm_device *drm)
379 {
380 	struct tegra_drm *tegra = drm->dev_private;
381 
382 	tegra_fbdev_free(tegra->fbdev);
383 }
384 
385 void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev)
386 {
387 	if (fbdev) {
388 		drm_modeset_lock_all(fbdev->base.dev);
389 		drm_fb_helper_restore_fbdev_mode(&fbdev->base);
390 		drm_modeset_unlock_all(fbdev->base.dev);
391 	}
392 }
393