xref: /linux/drivers/gpu/drm/exynos/exynos_drm_fbdev.c (revision b43ab901d671e3e3cad425ea5e9a3c74e266dcdd)
1 /* exynos_drm_fbdev.c
2  *
3  * Copyright (c) 2011 Samsung Electronics Co., Ltd.
4  * Authors:
5  *	Inki Dae <inki.dae@samsung.com>
6  *	Joonyoung Shim <jy0922.shim@samsung.com>
7  *	Seung-Woo Kim <sw0312.kim@samsung.com>
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining a
10  * copy of this software and associated documentation files (the "Software"),
11  * to deal in the Software without restriction, including without limitation
12  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13  * and/or sell copies of the Software, and to permit persons to whom the
14  * Software is furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice (including the next
17  * paragraph) shall be included in all copies or substantial portions of the
18  * Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
23  * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
24  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
25  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
26  * OTHER DEALINGS IN THE SOFTWARE.
27  */
28 
29 #include "drmP.h"
30 #include "drm_crtc.h"
31 #include "drm_fb_helper.h"
32 #include "drm_crtc_helper.h"
33 
34 #include "exynos_drm_drv.h"
35 #include "exynos_drm_fb.h"
36 #include "exynos_drm_gem.h"
37 
38 #define MAX_CONNECTOR		4
39 #define PREFERRED_BPP		32
40 
41 #define to_exynos_fbdev(x)	container_of(x, struct exynos_drm_fbdev,\
42 				drm_fb_helper)
43 
44 struct exynos_drm_fbdev {
45 	struct drm_fb_helper		drm_fb_helper;
46 	struct exynos_drm_gem_obj	*exynos_gem_obj;
47 };
48 
49 static int exynos_drm_fbdev_set_par(struct fb_info *info)
50 {
51 	struct fb_var_screeninfo *var = &info->var;
52 
53 	switch (var->bits_per_pixel) {
54 	case 32:
55 	case 24:
56 	case 18:
57 	case 16:
58 	case 12:
59 		info->fix.visual = FB_VISUAL_TRUECOLOR;
60 		break;
61 	case 1:
62 		info->fix.visual = FB_VISUAL_MONO01;
63 		break;
64 	default:
65 		info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
66 		break;
67 	}
68 
69 	info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;
70 
71 	return drm_fb_helper_set_par(info);
72 }
73 
74 
75 static struct fb_ops exynos_drm_fb_ops = {
76 	.owner		= THIS_MODULE,
77 	.fb_fillrect	= cfb_fillrect,
78 	.fb_copyarea	= cfb_copyarea,
79 	.fb_imageblit	= cfb_imageblit,
80 	.fb_check_var	= drm_fb_helper_check_var,
81 	.fb_set_par	= exynos_drm_fbdev_set_par,
82 	.fb_blank	= drm_fb_helper_blank,
83 	.fb_pan_display	= drm_fb_helper_pan_display,
84 	.fb_setcmap	= drm_fb_helper_setcmap,
85 };
86 
87 static int exynos_drm_fbdev_update(struct drm_fb_helper *helper,
88 				     struct drm_framebuffer *fb)
89 {
90 	struct fb_info *fbi = helper->fbdev;
91 	struct drm_device *dev = helper->dev;
92 	struct exynos_drm_gem_buf *buffer;
93 	unsigned int size = fb->width * fb->height * (fb->bits_per_pixel >> 3);
94 	unsigned long offset;
95 
96 	DRM_DEBUG_KMS("%s\n", __FILE__);
97 
98 	drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
99 	drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
100 
101 	/* RGB formats use only one buffer */
102 	buffer = exynos_drm_fb_buffer(fb, 0);
103 	if (!buffer) {
104 		DRM_LOG_KMS("buffer is null.\n");
105 		return -EFAULT;
106 	}
107 
108 	offset = fbi->var.xoffset * (fb->bits_per_pixel >> 3);
109 	offset += fbi->var.yoffset * fb->pitches[0];
110 
111 	dev->mode_config.fb_base = (resource_size_t)buffer->dma_addr;
112 	fbi->screen_base = buffer->kvaddr + offset;
113 	fbi->fix.smem_start = (unsigned long)(buffer->dma_addr + offset);
114 	fbi->screen_size = size;
115 	fbi->fix.smem_len = size;
116 
117 	return 0;
118 }
119 
120 static int exynos_drm_fbdev_create(struct drm_fb_helper *helper,
121 				    struct drm_fb_helper_surface_size *sizes)
122 {
123 	struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper);
124 	struct exynos_drm_gem_obj *exynos_gem_obj;
125 	struct drm_device *dev = helper->dev;
126 	struct fb_info *fbi;
127 	struct drm_mode_fb_cmd2 mode_cmd = { 0 };
128 	struct platform_device *pdev = dev->platformdev;
129 	unsigned long size;
130 	int ret;
131 
132 	DRM_DEBUG_KMS("%s\n", __FILE__);
133 
134 	DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d\n",
135 			sizes->surface_width, sizes->surface_height,
136 			sizes->surface_bpp);
137 
138 	mode_cmd.width = sizes->surface_width;
139 	mode_cmd.height = sizes->surface_height;
140 	mode_cmd.pitches[0] = sizes->surface_width * (sizes->surface_bpp >> 3);
141 	mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
142 							  sizes->surface_depth);
143 
144 	mutex_lock(&dev->struct_mutex);
145 
146 	fbi = framebuffer_alloc(0, &pdev->dev);
147 	if (!fbi) {
148 		DRM_ERROR("failed to allocate fb info.\n");
149 		ret = -ENOMEM;
150 		goto out;
151 	}
152 
153 	size = mode_cmd.pitches[0] * mode_cmd.height;
154 	exynos_gem_obj = exynos_drm_gem_create(dev, size);
155 	if (IS_ERR(exynos_gem_obj)) {
156 		ret = PTR_ERR(exynos_gem_obj);
157 		goto out;
158 	}
159 
160 	exynos_fbdev->exynos_gem_obj = exynos_gem_obj;
161 
162 	helper->fb = exynos_drm_framebuffer_init(dev, &mode_cmd,
163 			&exynos_gem_obj->base);
164 	if (IS_ERR_OR_NULL(helper->fb)) {
165 		DRM_ERROR("failed to create drm framebuffer.\n");
166 		ret = PTR_ERR(helper->fb);
167 		goto out;
168 	}
169 
170 	helper->fbdev = fbi;
171 
172 	fbi->par = helper;
173 	fbi->flags = FBINFO_FLAG_DEFAULT;
174 	fbi->fbops = &exynos_drm_fb_ops;
175 
176 	ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
177 	if (ret) {
178 		DRM_ERROR("failed to allocate cmap.\n");
179 		goto out;
180 	}
181 
182 	ret = exynos_drm_fbdev_update(helper, helper->fb);
183 	if (ret < 0) {
184 		fb_dealloc_cmap(&fbi->cmap);
185 		goto out;
186 	}
187 
188 /*
189  * if failed, all resources allocated above would be released by
190  * drm_mode_config_cleanup() when drm_load() had been called prior
191  * to any specific driver such as fimd or hdmi driver.
192  */
193 out:
194 	mutex_unlock(&dev->struct_mutex);
195 	return ret;
196 }
197 
198 static bool
199 exynos_drm_fbdev_is_samefb(struct drm_framebuffer *fb,
200 			    struct drm_fb_helper_surface_size *sizes)
201 {
202 	if (fb->width != sizes->surface_width)
203 		return false;
204 	if (fb->height != sizes->surface_height)
205 		return false;
206 	if (fb->bits_per_pixel != sizes->surface_bpp)
207 		return false;
208 	if (fb->depth != sizes->surface_depth)
209 		return false;
210 
211 	return true;
212 }
213 
214 static int exynos_drm_fbdev_recreate(struct drm_fb_helper *helper,
215 				      struct drm_fb_helper_surface_size *sizes)
216 {
217 	struct drm_device *dev = helper->dev;
218 	struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper);
219 	struct exynos_drm_gem_obj *exynos_gem_obj;
220 	struct drm_framebuffer *fb = helper->fb;
221 	struct drm_mode_fb_cmd2 mode_cmd = { 0 };
222 	unsigned long size;
223 
224 	DRM_DEBUG_KMS("%s\n", __FILE__);
225 
226 	if (exynos_drm_fbdev_is_samefb(fb, sizes))
227 		return 0;
228 
229 	mode_cmd.width = sizes->surface_width;
230 	mode_cmd.height = sizes->surface_height;
231 	mode_cmd.pitches[0] = sizes->surface_width * (sizes->surface_bpp >> 3);
232 	mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
233 							  sizes->surface_depth);
234 
235 	if (exynos_fbdev->exynos_gem_obj)
236 		exynos_drm_gem_destroy(exynos_fbdev->exynos_gem_obj);
237 
238 	if (fb->funcs->destroy)
239 		fb->funcs->destroy(fb);
240 
241 	size = mode_cmd.pitches[0] * mode_cmd.height;
242 	exynos_gem_obj = exynos_drm_gem_create(dev, size);
243 	if (IS_ERR(exynos_gem_obj))
244 		return PTR_ERR(exynos_gem_obj);
245 
246 	exynos_fbdev->exynos_gem_obj = exynos_gem_obj;
247 
248 	helper->fb = exynos_drm_framebuffer_init(dev, &mode_cmd,
249 			&exynos_gem_obj->base);
250 	if (IS_ERR_OR_NULL(helper->fb)) {
251 		DRM_ERROR("failed to create drm framebuffer.\n");
252 		return PTR_ERR(helper->fb);
253 	}
254 
255 	return exynos_drm_fbdev_update(helper, helper->fb);
256 }
257 
258 static int exynos_drm_fbdev_probe(struct drm_fb_helper *helper,
259 				   struct drm_fb_helper_surface_size *sizes)
260 {
261 	int ret = 0;
262 
263 	DRM_DEBUG_KMS("%s\n", __FILE__);
264 
265 	if (!helper->fb) {
266 		ret = exynos_drm_fbdev_create(helper, sizes);
267 		if (ret < 0) {
268 			DRM_ERROR("failed to create fbdev.\n");
269 			return ret;
270 		}
271 
272 		/*
273 		 * fb_helper expects a value more than 1 if succeed
274 		 * because register_framebuffer() should be called.
275 		 */
276 		ret = 1;
277 	} else {
278 		ret = exynos_drm_fbdev_recreate(helper, sizes);
279 		if (ret < 0) {
280 			DRM_ERROR("failed to reconfigure fbdev\n");
281 			return ret;
282 		}
283 	}
284 
285 	return ret;
286 }
287 
288 static struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
289 	.fb_probe =	exynos_drm_fbdev_probe,
290 };
291 
292 int exynos_drm_fbdev_init(struct drm_device *dev)
293 {
294 	struct exynos_drm_fbdev *fbdev;
295 	struct exynos_drm_private *private = dev->dev_private;
296 	struct drm_fb_helper *helper;
297 	unsigned int num_crtc;
298 	int ret;
299 
300 	DRM_DEBUG_KMS("%s\n", __FILE__);
301 
302 	if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector)
303 		return 0;
304 
305 	fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
306 	if (!fbdev) {
307 		DRM_ERROR("failed to allocate drm fbdev.\n");
308 		return -ENOMEM;
309 	}
310 
311 	private->fb_helper = helper = &fbdev->drm_fb_helper;
312 	helper->funcs = &exynos_drm_fb_helper_funcs;
313 
314 	num_crtc = dev->mode_config.num_crtc;
315 
316 	ret = drm_fb_helper_init(dev, helper, num_crtc, MAX_CONNECTOR);
317 	if (ret < 0) {
318 		DRM_ERROR("failed to initialize drm fb helper.\n");
319 		goto err_init;
320 	}
321 
322 	ret = drm_fb_helper_single_add_all_connectors(helper);
323 	if (ret < 0) {
324 		DRM_ERROR("failed to register drm_fb_helper_connector.\n");
325 		goto err_setup;
326 
327 	}
328 
329 	ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP);
330 	if (ret < 0) {
331 		DRM_ERROR("failed to set up hw configuration.\n");
332 		goto err_setup;
333 	}
334 
335 	return 0;
336 
337 err_setup:
338 	drm_fb_helper_fini(helper);
339 
340 err_init:
341 	private->fb_helper = NULL;
342 	kfree(fbdev);
343 
344 	return ret;
345 }
346 
347 static void exynos_drm_fbdev_destroy(struct drm_device *dev,
348 				      struct drm_fb_helper *fb_helper)
349 {
350 	struct drm_framebuffer *fb;
351 
352 	/* release drm framebuffer and real buffer */
353 	if (fb_helper->fb && fb_helper->fb->funcs) {
354 		fb = fb_helper->fb;
355 		if (fb && fb->funcs->destroy)
356 			fb->funcs->destroy(fb);
357 	}
358 
359 	/* release linux framebuffer */
360 	if (fb_helper->fbdev) {
361 		struct fb_info *info;
362 		int ret;
363 
364 		info = fb_helper->fbdev;
365 		ret = unregister_framebuffer(info);
366 		if (ret < 0)
367 			DRM_DEBUG_KMS("failed unregister_framebuffer()\n");
368 
369 		if (info->cmap.len)
370 			fb_dealloc_cmap(&info->cmap);
371 
372 		framebuffer_release(info);
373 	}
374 
375 	drm_fb_helper_fini(fb_helper);
376 }
377 
378 void exynos_drm_fbdev_fini(struct drm_device *dev)
379 {
380 	struct exynos_drm_private *private = dev->dev_private;
381 	struct exynos_drm_fbdev *fbdev;
382 
383 	if (!private || !private->fb_helper)
384 		return;
385 
386 	fbdev = to_exynos_fbdev(private->fb_helper);
387 
388 	if (fbdev->exynos_gem_obj)
389 		exynos_drm_gem_destroy(fbdev->exynos_gem_obj);
390 
391 	exynos_drm_fbdev_destroy(dev, private->fb_helper);
392 	kfree(fbdev);
393 	private->fb_helper = NULL;
394 }
395 
396 void exynos_drm_fbdev_restore_mode(struct drm_device *dev)
397 {
398 	struct exynos_drm_private *private = dev->dev_private;
399 
400 	if (!private || !private->fb_helper)
401 		return;
402 
403 	drm_fb_helper_restore_fbdev_mode(private->fb_helper);
404 }
405 
406 int exynos_drm_fbdev_reinit(struct drm_device *dev)
407 {
408 	struct exynos_drm_private *private = dev->dev_private;
409 	struct drm_fb_helper *fb_helper;
410 	int ret;
411 
412 	if (!private)
413 		return -EINVAL;
414 
415 	/*
416 	 * if all sub drivers were unloaded then num_connector is 0
417 	 * so at this time, the framebuffers also should be destroyed.
418 	 */
419 	if (!dev->mode_config.num_connector) {
420 		exynos_drm_fbdev_fini(dev);
421 		return 0;
422 	}
423 
424 	fb_helper = private->fb_helper;
425 
426 	if (fb_helper) {
427 		struct list_head temp_list;
428 
429 		INIT_LIST_HEAD(&temp_list);
430 
431 		/*
432 		 * fb_helper is reintialized but kernel fb is reused
433 		 * so kernel_fb_list need to be backuped and restored
434 		 */
435 		if (!list_empty(&fb_helper->kernel_fb_list))
436 			list_replace_init(&fb_helper->kernel_fb_list,
437 					&temp_list);
438 
439 		drm_fb_helper_fini(fb_helper);
440 
441 		ret = drm_fb_helper_init(dev, fb_helper,
442 				dev->mode_config.num_crtc, MAX_CONNECTOR);
443 		if (ret < 0) {
444 			DRM_ERROR("failed to initialize drm fb helper\n");
445 			return ret;
446 		}
447 
448 		if (!list_empty(&temp_list))
449 			list_replace(&temp_list, &fb_helper->kernel_fb_list);
450 
451 		ret = drm_fb_helper_single_add_all_connectors(fb_helper);
452 		if (ret < 0) {
453 			DRM_ERROR("failed to add fb helper to connectors\n");
454 			goto err;
455 		}
456 
457 		ret = drm_fb_helper_initial_config(fb_helper, PREFERRED_BPP);
458 		if (ret < 0) {
459 			DRM_ERROR("failed to set up hw configuration.\n");
460 			goto err;
461 		}
462 	} else {
463 		/*
464 		 * if drm_load() failed whem drm load() was called prior
465 		 * to specific drivers, fb_helper must be NULL and so
466 		 * this fuction should be called again to re-initialize and
467 		 * re-configure the fb helper. it means that this function
468 		 * has been called by the specific drivers.
469 		 */
470 		ret = exynos_drm_fbdev_init(dev);
471 	}
472 
473 	return ret;
474 
475 err:
476 	/*
477 	 * if drm_load() failed when drm load() was called prior
478 	 * to specific drivers, the fb_helper must be NULL and so check it.
479 	 */
480 	if (fb_helper)
481 		drm_fb_helper_fini(fb_helper);
482 
483 	return ret;
484 }
485 
486 MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
487 MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
488 MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
489 MODULE_DESCRIPTION("Samsung SoC DRM FBDEV Driver");
490 MODULE_LICENSE("GPL");
491