xref: /linux/drivers/gpu/drm/exynos/exynos_drm_fbdev.c (revision aa6b2b6cd43e4a23c2a220382a8b385b087d8bca)
11c248b7dSInki Dae /* exynos_drm_fbdev.c
21c248b7dSInki Dae  *
31c248b7dSInki Dae  * Copyright (c) 2011 Samsung Electronics Co., Ltd.
41c248b7dSInki Dae  * Authors:
51c248b7dSInki Dae  *	Inki Dae <inki.dae@samsung.com>
61c248b7dSInki Dae  *	Joonyoung Shim <jy0922.shim@samsung.com>
71c248b7dSInki Dae  *	Seung-Woo Kim <sw0312.kim@samsung.com>
81c248b7dSInki Dae  *
91c248b7dSInki Dae  * Permission is hereby granted, free of charge, to any person obtaining a
101c248b7dSInki Dae  * copy of this software and associated documentation files (the "Software"),
111c248b7dSInki Dae  * to deal in the Software without restriction, including without limitation
121c248b7dSInki Dae  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
131c248b7dSInki Dae  * and/or sell copies of the Software, and to permit persons to whom the
141c248b7dSInki Dae  * Software is furnished to do so, subject to the following conditions:
151c248b7dSInki Dae  *
161c248b7dSInki Dae  * The above copyright notice and this permission notice (including the next
171c248b7dSInki Dae  * paragraph) shall be included in all copies or substantial portions of the
181c248b7dSInki Dae  * Software.
191c248b7dSInki Dae  *
201c248b7dSInki Dae  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
211c248b7dSInki Dae  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
221c248b7dSInki Dae  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
231c248b7dSInki Dae  * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
241c248b7dSInki Dae  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
251c248b7dSInki Dae  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
261c248b7dSInki Dae  * OTHER DEALINGS IN THE SOFTWARE.
271c248b7dSInki Dae  */
281c248b7dSInki Dae 
291c248b7dSInki Dae #include "drmP.h"
301c248b7dSInki Dae #include "drm_crtc.h"
311c248b7dSInki Dae #include "drm_fb_helper.h"
321c248b7dSInki Dae #include "drm_crtc_helper.h"
331c248b7dSInki Dae 
341c248b7dSInki Dae #include "exynos_drm_drv.h"
351c248b7dSInki Dae #include "exynos_drm_fb.h"
3619c8b834SInki Dae #include "exynos_drm_buf.h"
371c248b7dSInki Dae 
381c248b7dSInki Dae #define MAX_CONNECTOR		4
391c248b7dSInki Dae #define PREFERRED_BPP		32
401c248b7dSInki Dae 
411c248b7dSInki Dae #define to_exynos_fbdev(x)	container_of(x, struct exynos_drm_fbdev,\
421c248b7dSInki Dae 				drm_fb_helper)
431c248b7dSInki Dae 
441c248b7dSInki Dae struct exynos_drm_fbdev {
451c248b7dSInki Dae 	struct drm_fb_helper	drm_fb_helper;
461c248b7dSInki Dae 	struct drm_framebuffer	*fb;
471c248b7dSInki Dae };
481c248b7dSInki Dae 
491c248b7dSInki Dae static int exynos_drm_fbdev_set_par(struct fb_info *info)
501c248b7dSInki Dae {
511c248b7dSInki Dae 	struct fb_var_screeninfo *var = &info->var;
521c248b7dSInki Dae 
531c248b7dSInki Dae 	switch (var->bits_per_pixel) {
541c248b7dSInki Dae 	case 32:
551c248b7dSInki Dae 	case 24:
561c248b7dSInki Dae 	case 18:
571c248b7dSInki Dae 	case 16:
581c248b7dSInki Dae 	case 12:
591c248b7dSInki Dae 		info->fix.visual = FB_VISUAL_TRUECOLOR;
601c248b7dSInki Dae 		break;
611c248b7dSInki Dae 	case 1:
621c248b7dSInki Dae 		info->fix.visual = FB_VISUAL_MONO01;
631c248b7dSInki Dae 		break;
641c248b7dSInki Dae 	default:
651c248b7dSInki Dae 		info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
661c248b7dSInki Dae 		break;
671c248b7dSInki Dae 	}
681c248b7dSInki Dae 
691c248b7dSInki Dae 	info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;
701c248b7dSInki Dae 
711c248b7dSInki Dae 	return drm_fb_helper_set_par(info);
721c248b7dSInki Dae }
731c248b7dSInki Dae 
741c248b7dSInki Dae 
751c248b7dSInki Dae static struct fb_ops exynos_drm_fb_ops = {
761c248b7dSInki Dae 	.owner		= THIS_MODULE,
771c248b7dSInki Dae 	.fb_fillrect	= cfb_fillrect,
781c248b7dSInki Dae 	.fb_copyarea	= cfb_copyarea,
791c248b7dSInki Dae 	.fb_imageblit	= cfb_imageblit,
801c248b7dSInki Dae 	.fb_check_var	= drm_fb_helper_check_var,
811c248b7dSInki Dae 	.fb_set_par	= exynos_drm_fbdev_set_par,
821c248b7dSInki Dae 	.fb_blank	= drm_fb_helper_blank,
831c248b7dSInki Dae 	.fb_pan_display	= drm_fb_helper_pan_display,
841c248b7dSInki Dae 	.fb_setcmap	= drm_fb_helper_setcmap,
851c248b7dSInki Dae };
861c248b7dSInki Dae 
8719c8b834SInki Dae static int exynos_drm_fbdev_update(struct drm_fb_helper *helper,
88*aa6b2b6cSSeung-Woo Kim 				     struct drm_framebuffer *fb)
891c248b7dSInki Dae {
901c248b7dSInki Dae 	struct fb_info *fbi = helper->fbdev;
911c248b7dSInki Dae 	struct drm_device *dev = helper->dev;
921c248b7dSInki Dae 	struct exynos_drm_fbdev *exynos_fb = to_exynos_fbdev(helper);
9319c8b834SInki Dae 	struct exynos_drm_buf_entry *entry;
94*aa6b2b6cSSeung-Woo Kim 	unsigned int size = fb->width * fb->height * (fb->bits_per_pixel >> 3);
9519c8b834SInki Dae 	unsigned long offset;
961c248b7dSInki Dae 
971c248b7dSInki Dae 	DRM_DEBUG_KMS("%s\n", __FILE__);
981c248b7dSInki Dae 
991c248b7dSInki Dae 	exynos_fb->fb = fb;
1001c248b7dSInki Dae 
1011c248b7dSInki Dae 	drm_fb_helper_fill_fix(fbi, fb->pitch, fb->depth);
102*aa6b2b6cSSeung-Woo Kim 	drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
1031c248b7dSInki Dae 
10419c8b834SInki Dae 	entry = exynos_drm_fb_get_buf(fb);
10519c8b834SInki Dae 	if (!entry) {
10619c8b834SInki Dae 		DRM_LOG_KMS("entry is null.\n");
10719c8b834SInki Dae 		return -EFAULT;
10819c8b834SInki Dae 	}
1091c248b7dSInki Dae 
11019c8b834SInki Dae 	offset = fbi->var.xoffset * (fb->bits_per_pixel >> 3);
11119c8b834SInki Dae 	offset += fbi->var.yoffset * fb->pitch;
1121c248b7dSInki Dae 
11319c8b834SInki Dae 	dev->mode_config.fb_base = entry->paddr;
11419c8b834SInki Dae 	fbi->screen_base = entry->vaddr + offset;
11519c8b834SInki Dae 	fbi->fix.smem_start = entry->paddr + offset;
1161c248b7dSInki Dae 	fbi->screen_size = size;
1171c248b7dSInki Dae 	fbi->fix.smem_len = size;
11819c8b834SInki Dae 
11919c8b834SInki Dae 	return 0;
1201c248b7dSInki Dae }
1211c248b7dSInki Dae 
1221c248b7dSInki Dae static int exynos_drm_fbdev_create(struct drm_fb_helper *helper,
1231c248b7dSInki Dae 				    struct drm_fb_helper_surface_size *sizes)
1241c248b7dSInki Dae {
1251c248b7dSInki Dae 	struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper);
1261c248b7dSInki Dae 	struct drm_device *dev = helper->dev;
1271c248b7dSInki Dae 	struct fb_info *fbi;
1281c248b7dSInki Dae 	struct drm_mode_fb_cmd mode_cmd = { 0 };
1291c248b7dSInki Dae 	struct platform_device *pdev = dev->platformdev;
1301c248b7dSInki Dae 	int ret;
1311c248b7dSInki Dae 
1321c248b7dSInki Dae 	DRM_DEBUG_KMS("%s\n", __FILE__);
1331c248b7dSInki Dae 
1341c248b7dSInki Dae 	DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d\n",
1351c248b7dSInki Dae 			sizes->surface_width, sizes->surface_height,
1361c248b7dSInki Dae 			sizes->surface_bpp);
1371c248b7dSInki Dae 
1381c248b7dSInki Dae 	mode_cmd.width = sizes->surface_width;
1391c248b7dSInki Dae 	mode_cmd.height = sizes->surface_height;
1401c248b7dSInki Dae 	mode_cmd.bpp = sizes->surface_bpp;
1411c248b7dSInki Dae 	mode_cmd.depth = sizes->surface_depth;
1421c248b7dSInki Dae 
1431c248b7dSInki Dae 	mutex_lock(&dev->struct_mutex);
1441c248b7dSInki Dae 
1451c248b7dSInki Dae 	fbi = framebuffer_alloc(0, &pdev->dev);
1461c248b7dSInki Dae 	if (!fbi) {
1471c248b7dSInki Dae 		DRM_ERROR("failed to allocate fb info.\n");
1481c248b7dSInki Dae 		ret = -ENOMEM;
1491c248b7dSInki Dae 		goto out;
1501c248b7dSInki Dae 	}
1511c248b7dSInki Dae 
1521c248b7dSInki Dae 	exynos_fbdev->fb = exynos_drm_fb_create(dev, NULL, &mode_cmd);
1531c248b7dSInki Dae 	if (IS_ERR_OR_NULL(exynos_fbdev->fb)) {
1541c248b7dSInki Dae 		DRM_ERROR("failed to create drm framebuffer.\n");
1551c248b7dSInki Dae 		ret = PTR_ERR(exynos_fbdev->fb);
1561c248b7dSInki Dae 		goto out;
1571c248b7dSInki Dae 	}
1581c248b7dSInki Dae 
1591c248b7dSInki Dae 	helper->fb = exynos_fbdev->fb;
1601c248b7dSInki Dae 	helper->fbdev = fbi;
1611c248b7dSInki Dae 
1621c248b7dSInki Dae 	fbi->par = helper;
1631c248b7dSInki Dae 	fbi->flags = FBINFO_FLAG_DEFAULT;
1641c248b7dSInki Dae 	fbi->fbops = &exynos_drm_fb_ops;
1651c248b7dSInki Dae 
1661c248b7dSInki Dae 	ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
1671c248b7dSInki Dae 	if (ret) {
1681c248b7dSInki Dae 		DRM_ERROR("failed to allocate cmap.\n");
1691c248b7dSInki Dae 		goto out;
1701c248b7dSInki Dae 	}
1711c248b7dSInki Dae 
172*aa6b2b6cSSeung-Woo Kim 	ret = exynos_drm_fbdev_update(helper, helper->fb);
17319c8b834SInki Dae 	if (ret < 0)
17419c8b834SInki Dae 		fb_dealloc_cmap(&fbi->cmap);
1751c248b7dSInki Dae 
1761c248b7dSInki Dae /*
1771c248b7dSInki Dae  * if failed, all resources allocated above would be released by
1781c248b7dSInki Dae  * drm_mode_config_cleanup() when drm_load() had been called prior
1791c248b7dSInki Dae  * to any specific driver such as fimd or hdmi driver.
1801c248b7dSInki Dae  */
1811c248b7dSInki Dae out:
1821c248b7dSInki Dae 	mutex_unlock(&dev->struct_mutex);
1831c248b7dSInki Dae 	return ret;
1841c248b7dSInki Dae }
1851c248b7dSInki Dae 
1861c248b7dSInki Dae static bool
1871c248b7dSInki Dae exynos_drm_fbdev_is_samefb(struct drm_framebuffer *fb,
1881c248b7dSInki Dae 			    struct drm_fb_helper_surface_size *sizes)
1891c248b7dSInki Dae {
1901c248b7dSInki Dae 	if (fb->width != sizes->surface_width)
1911c248b7dSInki Dae 		return false;
1921c248b7dSInki Dae 	if (fb->height != sizes->surface_height)
1931c248b7dSInki Dae 		return false;
1941c248b7dSInki Dae 	if (fb->bits_per_pixel != sizes->surface_bpp)
1951c248b7dSInki Dae 		return false;
1961c248b7dSInki Dae 	if (fb->depth != sizes->surface_depth)
1971c248b7dSInki Dae 		return false;
1981c248b7dSInki Dae 
1991c248b7dSInki Dae 	return true;
2001c248b7dSInki Dae }
2011c248b7dSInki Dae 
2021c248b7dSInki Dae static int exynos_drm_fbdev_recreate(struct drm_fb_helper *helper,
2031c248b7dSInki Dae 				      struct drm_fb_helper_surface_size *sizes)
2041c248b7dSInki Dae {
2051c248b7dSInki Dae 	struct drm_device *dev = helper->dev;
2061c248b7dSInki Dae 	struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper);
2071c248b7dSInki Dae 	struct drm_framebuffer *fb = exynos_fbdev->fb;
2081c248b7dSInki Dae 	struct drm_mode_fb_cmd mode_cmd = { 0 };
2091c248b7dSInki Dae 
2101c248b7dSInki Dae 	DRM_DEBUG_KMS("%s\n", __FILE__);
2111c248b7dSInki Dae 
2121c248b7dSInki Dae 	if (helper->fb != fb) {
2131c248b7dSInki Dae 		DRM_ERROR("drm framebuffer is different\n");
2141c248b7dSInki Dae 		return -EINVAL;
2151c248b7dSInki Dae 	}
2161c248b7dSInki Dae 
2171c248b7dSInki Dae 	if (exynos_drm_fbdev_is_samefb(fb, sizes))
2181c248b7dSInki Dae 		return 0;
2191c248b7dSInki Dae 
2201c248b7dSInki Dae 	mode_cmd.width = sizes->surface_width;
2211c248b7dSInki Dae 	mode_cmd.height = sizes->surface_height;
2221c248b7dSInki Dae 	mode_cmd.bpp = sizes->surface_bpp;
2231c248b7dSInki Dae 	mode_cmd.depth = sizes->surface_depth;
2241c248b7dSInki Dae 
2251c248b7dSInki Dae 	if (fb->funcs->destroy)
2261c248b7dSInki Dae 		fb->funcs->destroy(fb);
2271c248b7dSInki Dae 
2281c248b7dSInki Dae 	exynos_fbdev->fb = exynos_drm_fb_create(dev, NULL, &mode_cmd);
2291c248b7dSInki Dae 	if (IS_ERR(exynos_fbdev->fb)) {
2301c248b7dSInki Dae 		DRM_ERROR("failed to allocate fb.\n");
2311c248b7dSInki Dae 		return PTR_ERR(exynos_fbdev->fb);
2321c248b7dSInki Dae 	}
2331c248b7dSInki Dae 
2341c248b7dSInki Dae 	helper->fb = exynos_fbdev->fb;
235*aa6b2b6cSSeung-Woo Kim 	return exynos_drm_fbdev_update(helper, helper->fb);
2361c248b7dSInki Dae }
2371c248b7dSInki Dae 
2381c248b7dSInki Dae static int exynos_drm_fbdev_probe(struct drm_fb_helper *helper,
2391c248b7dSInki Dae 				   struct drm_fb_helper_surface_size *sizes)
2401c248b7dSInki Dae {
2411c248b7dSInki Dae 	int ret = 0;
2421c248b7dSInki Dae 
2431c248b7dSInki Dae 	DRM_DEBUG_KMS("%s\n", __FILE__);
2441c248b7dSInki Dae 
2451c248b7dSInki Dae 	if (!helper->fb) {
2461c248b7dSInki Dae 		ret = exynos_drm_fbdev_create(helper, sizes);
2471c248b7dSInki Dae 		if (ret < 0) {
2481c248b7dSInki Dae 			DRM_ERROR("failed to create fbdev.\n");
2491c248b7dSInki Dae 			return ret;
2501c248b7dSInki Dae 		}
2511c248b7dSInki Dae 
2521c248b7dSInki Dae 		/*
2531c248b7dSInki Dae 		 * fb_helper expects a value more than 1 if succeed
2541c248b7dSInki Dae 		 * because register_framebuffer() should be called.
2551c248b7dSInki Dae 		 */
2561c248b7dSInki Dae 		ret = 1;
2571c248b7dSInki Dae 	} else {
2581c248b7dSInki Dae 		ret = exynos_drm_fbdev_recreate(helper, sizes);
2591c248b7dSInki Dae 		if (ret < 0) {
2601c248b7dSInki Dae 			DRM_ERROR("failed to reconfigure fbdev\n");
2611c248b7dSInki Dae 			return ret;
2621c248b7dSInki Dae 		}
2631c248b7dSInki Dae 	}
2641c248b7dSInki Dae 
2651c248b7dSInki Dae 	return ret;
2661c248b7dSInki Dae }
2671c248b7dSInki Dae 
2681c248b7dSInki Dae static struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
2691c248b7dSInki Dae 	.fb_probe =	exynos_drm_fbdev_probe,
2701c248b7dSInki Dae };
2711c248b7dSInki Dae 
2721c248b7dSInki Dae int exynos_drm_fbdev_init(struct drm_device *dev)
2731c248b7dSInki Dae {
2741c248b7dSInki Dae 	struct exynos_drm_fbdev *fbdev;
2751c248b7dSInki Dae 	struct exynos_drm_private *private = dev->dev_private;
2761c248b7dSInki Dae 	struct drm_fb_helper *helper;
2771c248b7dSInki Dae 	unsigned int num_crtc;
2781c248b7dSInki Dae 	int ret;
2791c248b7dSInki Dae 
2801c248b7dSInki Dae 	DRM_DEBUG_KMS("%s\n", __FILE__);
2811c248b7dSInki Dae 
2821c248b7dSInki Dae 	if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector)
2831c248b7dSInki Dae 		return 0;
2841c248b7dSInki Dae 
2851c248b7dSInki Dae 	fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
2861c248b7dSInki Dae 	if (!fbdev) {
2871c248b7dSInki Dae 		DRM_ERROR("failed to allocate drm fbdev.\n");
2881c248b7dSInki Dae 		return -ENOMEM;
2891c248b7dSInki Dae 	}
2901c248b7dSInki Dae 
2911c248b7dSInki Dae 	private->fb_helper = helper = &fbdev->drm_fb_helper;
2921c248b7dSInki Dae 	helper->funcs = &exynos_drm_fb_helper_funcs;
2931c248b7dSInki Dae 
2941c248b7dSInki Dae 	num_crtc = dev->mode_config.num_crtc;
2951c248b7dSInki Dae 
2961c248b7dSInki Dae 	ret = drm_fb_helper_init(dev, helper, num_crtc, MAX_CONNECTOR);
2971c248b7dSInki Dae 	if (ret < 0) {
2981c248b7dSInki Dae 		DRM_ERROR("failed to initialize drm fb helper.\n");
2991c248b7dSInki Dae 		goto err_init;
3001c248b7dSInki Dae 	}
3011c248b7dSInki Dae 
3021c248b7dSInki Dae 	ret = drm_fb_helper_single_add_all_connectors(helper);
3031c248b7dSInki Dae 	if (ret < 0) {
3041c248b7dSInki Dae 		DRM_ERROR("failed to register drm_fb_helper_connector.\n");
3051c248b7dSInki Dae 		goto err_setup;
3061c248b7dSInki Dae 
3071c248b7dSInki Dae 	}
3081c248b7dSInki Dae 
3091c248b7dSInki Dae 	ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP);
3101c248b7dSInki Dae 	if (ret < 0) {
3111c248b7dSInki Dae 		DRM_ERROR("failed to set up hw configuration.\n");
3121c248b7dSInki Dae 		goto err_setup;
3131c248b7dSInki Dae 	}
3141c248b7dSInki Dae 
3151c248b7dSInki Dae 	return 0;
3161c248b7dSInki Dae 
3171c248b7dSInki Dae err_setup:
3181c248b7dSInki Dae 	drm_fb_helper_fini(helper);
3191c248b7dSInki Dae 
3201c248b7dSInki Dae err_init:
3211c248b7dSInki Dae 	private->fb_helper = NULL;
3221c248b7dSInki Dae 	kfree(fbdev);
3231c248b7dSInki Dae 
3241c248b7dSInki Dae 	return ret;
3251c248b7dSInki Dae }
3261c248b7dSInki Dae 
3271c248b7dSInki Dae static void exynos_drm_fbdev_destroy(struct drm_device *dev,
3281c248b7dSInki Dae 				      struct drm_fb_helper *fb_helper)
3291c248b7dSInki Dae {
3301c248b7dSInki Dae 	struct drm_framebuffer *fb;
3311c248b7dSInki Dae 
3321c248b7dSInki Dae 	/* release drm framebuffer and real buffer */
3331c248b7dSInki Dae 	if (fb_helper->fb && fb_helper->fb->funcs) {
3341c248b7dSInki Dae 		fb = fb_helper->fb;
3351c248b7dSInki Dae 		if (fb && fb->funcs->destroy)
3361c248b7dSInki Dae 			fb->funcs->destroy(fb);
3371c248b7dSInki Dae 	}
3381c248b7dSInki Dae 
3391c248b7dSInki Dae 	/* release linux framebuffer */
3401c248b7dSInki Dae 	if (fb_helper->fbdev) {
3411c248b7dSInki Dae 		struct fb_info *info;
3421c248b7dSInki Dae 		int ret;
3431c248b7dSInki Dae 
3441c248b7dSInki Dae 		info = fb_helper->fbdev;
3451c248b7dSInki Dae 		ret = unregister_framebuffer(info);
3461c248b7dSInki Dae 		if (ret < 0)
3471c248b7dSInki Dae 			DRM_DEBUG_KMS("failed unregister_framebuffer()\n");
3481c248b7dSInki Dae 
3491c248b7dSInki Dae 		if (info->cmap.len)
3501c248b7dSInki Dae 			fb_dealloc_cmap(&info->cmap);
3511c248b7dSInki Dae 
3521c248b7dSInki Dae 		framebuffer_release(info);
3531c248b7dSInki Dae 	}
3541c248b7dSInki Dae 
3551c248b7dSInki Dae 	drm_fb_helper_fini(fb_helper);
3561c248b7dSInki Dae }
3571c248b7dSInki Dae 
3581c248b7dSInki Dae void exynos_drm_fbdev_fini(struct drm_device *dev)
3591c248b7dSInki Dae {
3601c248b7dSInki Dae 	struct exynos_drm_private *private = dev->dev_private;
3611c248b7dSInki Dae 	struct exynos_drm_fbdev *fbdev;
3621c248b7dSInki Dae 
3631c248b7dSInki Dae 	if (!private || !private->fb_helper)
3641c248b7dSInki Dae 		return;
3651c248b7dSInki Dae 
3661c248b7dSInki Dae 	fbdev = to_exynos_fbdev(private->fb_helper);
3671c248b7dSInki Dae 
3681c248b7dSInki Dae 	exynos_drm_fbdev_destroy(dev, private->fb_helper);
3691c248b7dSInki Dae 	kfree(fbdev);
3701c248b7dSInki Dae 	private->fb_helper = NULL;
3711c248b7dSInki Dae }
3721c248b7dSInki Dae 
3731c248b7dSInki Dae void exynos_drm_fbdev_restore_mode(struct drm_device *dev)
3741c248b7dSInki Dae {
3751c248b7dSInki Dae 	struct exynos_drm_private *private = dev->dev_private;
3761c248b7dSInki Dae 
3771c248b7dSInki Dae 	if (!private || !private->fb_helper)
3781c248b7dSInki Dae 		return;
3791c248b7dSInki Dae 
3801c248b7dSInki Dae 	drm_fb_helper_restore_fbdev_mode(private->fb_helper);
3811c248b7dSInki Dae }
3821c248b7dSInki Dae 
3831c248b7dSInki Dae int exynos_drm_fbdev_reinit(struct drm_device *dev)
3841c248b7dSInki Dae {
3851c248b7dSInki Dae 	struct exynos_drm_private *private = dev->dev_private;
3861c248b7dSInki Dae 	struct drm_fb_helper *fb_helper;
3871c248b7dSInki Dae 	int ret;
3881c248b7dSInki Dae 
3891c248b7dSInki Dae 	if (!private)
3901c248b7dSInki Dae 		return -EINVAL;
3911c248b7dSInki Dae 
392f6b98252SInki Dae 	/*
393f6b98252SInki Dae 	 * if all sub drivers were unloaded then num_connector is 0
394f6b98252SInki Dae 	 * so at this time, the framebuffers also should be destroyed.
395f6b98252SInki Dae 	 */
3961c248b7dSInki Dae 	if (!dev->mode_config.num_connector) {
3971c248b7dSInki Dae 		exynos_drm_fbdev_fini(dev);
3981c248b7dSInki Dae 		return 0;
3991c248b7dSInki Dae 	}
4001c248b7dSInki Dae 
4011c248b7dSInki Dae 	fb_helper = private->fb_helper;
4021c248b7dSInki Dae 
4031c248b7dSInki Dae 	if (fb_helper) {
40484b46990SJoonyoung Shim 		struct list_head temp_list;
40584b46990SJoonyoung Shim 
40684b46990SJoonyoung Shim 		INIT_LIST_HEAD(&temp_list);
40784b46990SJoonyoung Shim 
40884b46990SJoonyoung Shim 		/*
40984b46990SJoonyoung Shim 		 * fb_helper is reintialized but kernel fb is reused
41084b46990SJoonyoung Shim 		 * so kernel_fb_list need to be backuped and restored
41184b46990SJoonyoung Shim 		 */
41284b46990SJoonyoung Shim 		if (!list_empty(&fb_helper->kernel_fb_list))
41384b46990SJoonyoung Shim 			list_replace_init(&fb_helper->kernel_fb_list,
41484b46990SJoonyoung Shim 					&temp_list);
41584b46990SJoonyoung Shim 
4161c248b7dSInki Dae 		drm_fb_helper_fini(fb_helper);
4171c248b7dSInki Dae 
4181c248b7dSInki Dae 		ret = drm_fb_helper_init(dev, fb_helper,
4191c248b7dSInki Dae 				dev->mode_config.num_crtc, MAX_CONNECTOR);
4201c248b7dSInki Dae 		if (ret < 0) {
4211c248b7dSInki Dae 			DRM_ERROR("failed to initialize drm fb helper\n");
4221c248b7dSInki Dae 			return ret;
4231c248b7dSInki Dae 		}
4241c248b7dSInki Dae 
42584b46990SJoonyoung Shim 		if (!list_empty(&temp_list))
42684b46990SJoonyoung Shim 			list_replace(&temp_list, &fb_helper->kernel_fb_list);
42784b46990SJoonyoung Shim 
4281c248b7dSInki Dae 		ret = drm_fb_helper_single_add_all_connectors(fb_helper);
4291c248b7dSInki Dae 		if (ret < 0) {
4301c248b7dSInki Dae 			DRM_ERROR("failed to add fb helper to connectors\n");
4311c248b7dSInki Dae 			goto err;
4321c248b7dSInki Dae 		}
4331c248b7dSInki Dae 
4341c248b7dSInki Dae 		ret = drm_fb_helper_initial_config(fb_helper, PREFERRED_BPP);
4351c248b7dSInki Dae 		if (ret < 0) {
4361c248b7dSInki Dae 			DRM_ERROR("failed to set up hw configuration.\n");
4371c248b7dSInki Dae 			goto err;
4381c248b7dSInki Dae 		}
4391c248b7dSInki Dae 	} else {
4401c248b7dSInki Dae 		/*
4411c248b7dSInki Dae 		 * if drm_load() failed whem drm load() was called prior
4421c248b7dSInki Dae 		 * to specific drivers, fb_helper must be NULL and so
4431c248b7dSInki Dae 		 * this fuction should be called again to re-initialize and
4441c248b7dSInki Dae 		 * re-configure the fb helper. it means that this function
4451c248b7dSInki Dae 		 * has been called by the specific drivers.
4461c248b7dSInki Dae 		 */
447f6b98252SInki Dae 		ret = exynos_drm_fbdev_init(dev);
4481c248b7dSInki Dae 	}
4491c248b7dSInki Dae 
450601b44e3SInki Dae 	return ret;
451601b44e3SInki Dae 
4521c248b7dSInki Dae err:
4531c248b7dSInki Dae 	/*
4541c248b7dSInki Dae 	 * if drm_load() failed when drm load() was called prior
4551c248b7dSInki Dae 	 * to specific drivers, the fb_helper must be NULL and so check it.
4561c248b7dSInki Dae 	 */
4571c248b7dSInki Dae 	if (fb_helper)
4581c248b7dSInki Dae 		drm_fb_helper_fini(fb_helper);
4591c248b7dSInki Dae 
4601c248b7dSInki Dae 	return ret;
4611c248b7dSInki Dae }
4621c248b7dSInki Dae 
4631c248b7dSInki Dae MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
4641c248b7dSInki Dae MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
4651c248b7dSInki Dae MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
4661c248b7dSInki Dae MODULE_DESCRIPTION("Samsung SoC DRM FBDEV Driver");
4671c248b7dSInki Dae MODULE_LICENSE("GPL");
468