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