xref: /linux/drivers/gpu/drm/exynos/exynos_drm_fbdev.c (revision cdd5b5a9761fd66d17586e4f4ba6588c70e640ea)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21c248b7dSInki Dae /* exynos_drm_fbdev.c
31c248b7dSInki Dae  *
41c248b7dSInki Dae  * Copyright (c) 2011 Samsung Electronics Co., Ltd.
51c248b7dSInki Dae  * Authors:
61c248b7dSInki Dae  *	Inki Dae <inki.dae@samsung.com>
71c248b7dSInki Dae  *	Joonyoung Shim <jy0922.shim@samsung.com>
81c248b7dSInki Dae  *	Seung-Woo Kim <sw0312.kim@samsung.com>
91c248b7dSInki Dae  */
101c248b7dSInki Dae 
11*ac9dc1b1SThomas Zimmermann #include <linux/fb.h>
12*ac9dc1b1SThomas Zimmermann 
1349953b70SThomas Zimmermann #include <drm/drm_crtc_helper.h>
1449953b70SThomas Zimmermann #include <drm/drm_drv.h>
15760285e7SDavid Howells #include <drm/drm_fb_helper.h>
16720cf96dSVille Syrjälä #include <drm/drm_framebuffer.h>
1789c258b5SThomas Zimmermann #include <drm/drm_gem_framebuffer_helper.h>
182043e6f6SThomas Zimmermann #include <drm/drm_prime.h>
19a1bfacf4SVikas Sajjan #include <drm/exynos_drm.h>
201c248b7dSInki Dae 
211c248b7dSInki Dae #include "exynos_drm_drv.h"
221c248b7dSInki Dae #include "exynos_drm_fb.h"
23e30655d0SMark Brown #include "exynos_drm_fbdev.h"
241c248b7dSInki Dae 
251c248b7dSInki Dae #define MAX_CONNECTOR		4
261c248b7dSInki Dae #define PREFERRED_BPP		32
271c248b7dSInki Dae 
exynos_drm_fb_mmap(struct fb_info * info,struct vm_area_struct * vma)2889c258b5SThomas Zimmermann static int exynos_drm_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
29dd265850SPrathyush K {
30dd265850SPrathyush K 	struct drm_fb_helper *helper = info->par;
3189c258b5SThomas Zimmermann 	struct drm_gem_object *obj = drm_gem_fb_get_obj(helper->fb, 0);
32dd265850SPrathyush K 
3389c258b5SThomas Zimmermann 	return drm_gem_prime_mmap(obj, vma);
34dd265850SPrathyush K }
35dd265850SPrathyush K 
exynos_drm_fb_destroy(struct fb_info * info)3649953b70SThomas Zimmermann static void exynos_drm_fb_destroy(struct fb_info *info)
3749953b70SThomas Zimmermann {
3849953b70SThomas Zimmermann 	struct drm_fb_helper *fb_helper = info->par;
3949953b70SThomas Zimmermann 	struct drm_framebuffer *fb = fb_helper->fb;
4049953b70SThomas Zimmermann 
4149953b70SThomas Zimmermann 	drm_fb_helper_fini(fb_helper);
4249953b70SThomas Zimmermann 
4349953b70SThomas Zimmermann 	drm_framebuffer_remove(fb);
4449953b70SThomas Zimmermann 
4549953b70SThomas Zimmermann 	drm_client_release(&fb_helper->client);
4649953b70SThomas Zimmermann 	drm_fb_helper_unprepare(fb_helper);
4749953b70SThomas Zimmermann 	kfree(fb_helper);
4849953b70SThomas Zimmermann }
4949953b70SThomas Zimmermann 
50b6ff753aSJani Nikula static const struct fb_ops exynos_drm_fb_ops = {
511c248b7dSInki Dae 	.owner		= THIS_MODULE,
52*ac9dc1b1SThomas Zimmermann 	__FB_DEFAULT_DMAMEM_OPS_RDWR,
532eec838cSStefan Christ 	DRM_FB_HELPER_DEFAULT_OPS,
54*ac9dc1b1SThomas Zimmermann 	__FB_DEFAULT_DMAMEM_OPS_DRAW,
55dd265850SPrathyush K 	.fb_mmap        = exynos_drm_fb_mmap,
5649953b70SThomas Zimmermann 	.fb_destroy	= exynos_drm_fb_destroy,
571c248b7dSInki Dae };
581c248b7dSInki Dae 
exynos_drm_fbdev_update(struct drm_fb_helper * helper,struct drm_fb_helper_surface_size * sizes,struct exynos_drm_gem * exynos_gem)5919c8b834SInki Dae static int exynos_drm_fbdev_update(struct drm_fb_helper *helper,
60ecbf1d5aSRob Clark 				   struct drm_fb_helper_surface_size *sizes,
61813fd67bSJoonyoung Shim 				   struct exynos_drm_gem *exynos_gem)
621c248b7dSInki Dae {
63ee885ca5SJoonyoung Shim 	struct fb_info *fbi;
64d7619960SJoonyoung Shim 	struct drm_framebuffer *fb = helper->fb;
65272725c7SVille Syrjälä 	unsigned int size = fb->width * fb->height * fb->format->cpp[0];
6619c8b834SInki Dae 	unsigned long offset;
671c248b7dSInki Dae 
687fd50bc3SThomas Zimmermann 	fbi = drm_fb_helper_alloc_info(helper);
69ee885ca5SJoonyoung Shim 	if (IS_ERR(fbi)) {
706f83d208SInki Dae 		DRM_DEV_ERROR(to_dma_dev(helper->dev),
716f83d208SInki Dae 			      "failed to allocate fb info.\n");
72ee885ca5SJoonyoung Shim 		return PTR_ERR(fbi);
73ee885ca5SJoonyoung Shim 	}
74ee885ca5SJoonyoung Shim 
75ee885ca5SJoonyoung Shim 	fbi->fbops = &exynos_drm_fb_ops;
76ee885ca5SJoonyoung Shim 
77fb68e596SDaniel Vetter 	drm_fb_helper_fill_info(fbi, helper, sizes);
781c248b7dSInki Dae 
79272725c7SVille Syrjälä 	offset = fbi->var.xoffset * fb->format->cpp[0];
8001f2c773SVille Syrjälä 	offset += fbi->var.yoffset * fb->pitches[0];
811c248b7dSInki Dae 
82d4035d10SMarek Szyprowski 	fbi->flags |= FBINFO_VIRTFB;
831c248b7dSInki Dae 	fbi->screen_buffer = exynos_gem->kvaddr + offset;
8471b1f195SDaniel Kurtz 	fbi->screen_size = size;
8519c8b834SInki Dae 	fbi->fix.smem_len = size;
8619c8b834SInki Dae 
871c248b7dSInki Dae 	return 0;
881c248b7dSInki Dae }
891c248b7dSInki Dae 
exynos_drm_fbdev_create(struct drm_fb_helper * helper,struct drm_fb_helper_surface_size * sizes)901c248b7dSInki Dae static int exynos_drm_fbdev_create(struct drm_fb_helper *helper,
911c248b7dSInki Dae 				    struct drm_fb_helper_surface_size *sizes)
92813fd67bSJoonyoung Shim {
931c248b7dSInki Dae 	struct exynos_drm_gem *exynos_gem;
94a794d57dSJoonyoung Shim 	struct drm_device *dev = helper->dev;
95e1533c08SJoonyoung Shim 	struct drm_mode_fb_cmd2 mode_cmd = { 0 };
961c248b7dSInki Dae 	unsigned long size;
971c248b7dSInki Dae 	int ret;
986be90056SInki Dae 
996be90056SInki Dae 	DRM_DEV_DEBUG_KMS(dev->dev,
1001c248b7dSInki Dae 			  "surface width(%d), height(%d) and bpp(%d\n",
1011c248b7dSInki Dae 			  sizes->surface_width, sizes->surface_height,
1021c248b7dSInki Dae 			  sizes->surface_bpp);
1031c248b7dSInki Dae 
1041c248b7dSInki Dae 	mode_cmd.width = sizes->surface_width;
105a794d57dSJoonyoung Shim 	mode_cmd.height = sizes->surface_height;
106a794d57dSJoonyoung Shim 	mode_cmd.pitches[0] = sizes->surface_width * (sizes->surface_bpp >> 3);
107a794d57dSJoonyoung Shim 	mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
1081c248b7dSInki Dae 							  sizes->surface_depth);
109e1533c08SJoonyoung Shim 
1102b35892eSInki Dae 	size = mode_cmd.pitches[0] * mode_cmd.height;
1119940d9d9SMarek Szyprowski 
1122f424200SDaniel Vetter 	exynos_gem = exynos_drm_gem_create(dev, EXYNOS_BO_WC, size, true);
1132f424200SDaniel Vetter 	if (IS_ERR(exynos_gem))
1141c248b7dSInki Dae 		return PTR_ERR(exynos_gem);
115813fd67bSJoonyoung Shim 
116813fd67bSJoonyoung Shim 	helper->fb =
11741eab402SSachin Kamat 		exynos_drm_framebuffer_init(dev, &mode_cmd, &exynos_gem, 1);
1186f83d208SInki Dae 	if (IS_ERR(helper->fb)) {
119e1533c08SJoonyoung Shim 		DRM_DEV_ERROR(dev->dev, "failed to create drm framebuffer.\n");
120662aa6d7SInki Dae 		ret = PTR_ERR(helper->fb);
121e1533c08SJoonyoung Shim 		goto err_destroy_gem;
122e1533c08SJoonyoung Shim 	}
123813fd67bSJoonyoung Shim 
124662aa6d7SInki Dae 	ret = exynos_drm_fbdev_update(helper, sizes, exynos_gem);
1257c7d4507SArchit Taneja 	if (ret < 0)
126662aa6d7SInki Dae 		goto err_destroy_framebuffer;
12749953b70SThomas Zimmermann 
128662aa6d7SInki Dae 	return 0;
129662aa6d7SInki Dae 
130662aa6d7SInki Dae err_destroy_framebuffer:
13149953b70SThomas Zimmermann 	drm_framebuffer_cleanup(helper->fb);
132662aa6d7SInki Dae 	helper->fb = NULL;
133813fd67bSJoonyoung Shim err_destroy_gem:
1341c248b7dSInki Dae 	exynos_drm_gem_destroy(exynos_gem);
1351c248b7dSInki Dae 	return ret;
1361c248b7dSInki Dae }
1373a493879SThierry Reding 
138cd5428a5SDaniel Vetter static const struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
1391c248b7dSInki Dae 	.fb_probe =	exynos_drm_fbdev_create,
1401c248b7dSInki Dae };
14199286486SThomas Zimmermann 
14299286486SThomas Zimmermann /*
14399286486SThomas Zimmermann  * struct drm_client
14499286486SThomas Zimmermann  */
14599286486SThomas Zimmermann 
exynos_drm_fbdev_client_unregister(struct drm_client_dev * client)14649953b70SThomas Zimmermann static void exynos_drm_fbdev_client_unregister(struct drm_client_dev *client)
14749953b70SThomas Zimmermann {
14849953b70SThomas Zimmermann 	struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
14949953b70SThomas Zimmermann 
15049953b70SThomas Zimmermann 	if (fb_helper->info) {
15149953b70SThomas Zimmermann 		drm_fb_helper_unregister_info(fb_helper);
15249953b70SThomas Zimmermann 	} else {
15349953b70SThomas Zimmermann 		drm_client_release(&fb_helper->client);
15449953b70SThomas Zimmermann 		drm_fb_helper_unprepare(fb_helper);
15549953b70SThomas Zimmermann 		kfree(fb_helper);
15649953b70SThomas Zimmermann 	}
15799286486SThomas Zimmermann }
15899286486SThomas Zimmermann 
exynos_drm_fbdev_client_restore(struct drm_client_dev * client)15999286486SThomas Zimmermann static int exynos_drm_fbdev_client_restore(struct drm_client_dev *client)
16049953b70SThomas Zimmermann {
16149953b70SThomas Zimmermann 	drm_fb_helper_lastclose(client->dev);
16299286486SThomas Zimmermann 
16399286486SThomas Zimmermann 	return 0;
16499286486SThomas Zimmermann }
16599286486SThomas Zimmermann 
exynos_drm_fbdev_client_hotplug(struct drm_client_dev * client)16699286486SThomas Zimmermann static int exynos_drm_fbdev_client_hotplug(struct drm_client_dev *client)
16749953b70SThomas Zimmermann {
16849953b70SThomas Zimmermann 	struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
16949953b70SThomas Zimmermann 	struct drm_device *dev = client->dev;
17049953b70SThomas Zimmermann 	int ret;
17149953b70SThomas Zimmermann 
17249953b70SThomas Zimmermann 	if (dev->fb_helper)
17349953b70SThomas Zimmermann 		return drm_fb_helper_hotplug_event(dev->fb_helper);
17449953b70SThomas Zimmermann 
17549953b70SThomas Zimmermann 	ret = drm_fb_helper_init(dev, fb_helper);
17649953b70SThomas Zimmermann 	if (ret)
17749953b70SThomas Zimmermann 		goto err_drm_err;
17849953b70SThomas Zimmermann 
17949953b70SThomas Zimmermann 	if (!drm_drv_uses_atomic_modeset(dev))
18049953b70SThomas Zimmermann 		drm_helper_disable_unused_functions(dev);
18149953b70SThomas Zimmermann 
18249953b70SThomas Zimmermann 	ret = drm_fb_helper_initial_config(fb_helper);
18349953b70SThomas Zimmermann 	if (ret)
18449953b70SThomas Zimmermann 		goto err_drm_fb_helper_fini;
18599286486SThomas Zimmermann 
18649953b70SThomas Zimmermann 	return 0;
18749953b70SThomas Zimmermann 
18849953b70SThomas Zimmermann err_drm_fb_helper_fini:
18949953b70SThomas Zimmermann 	drm_fb_helper_fini(fb_helper);
19049953b70SThomas Zimmermann err_drm_err:
19149953b70SThomas Zimmermann 	drm_err(dev, "Failed to setup fbdev emulation (ret=%d)\n", ret);
19299286486SThomas Zimmermann 	return ret;
19399286486SThomas Zimmermann }
19499286486SThomas Zimmermann 
19599286486SThomas Zimmermann static const struct drm_client_funcs exynos_drm_fbdev_client_funcs = {
19699286486SThomas Zimmermann 	.owner		= THIS_MODULE,
19799286486SThomas Zimmermann 	.unregister	= exynos_drm_fbdev_client_unregister,
19899286486SThomas Zimmermann 	.restore	= exynos_drm_fbdev_client_restore,
19999286486SThomas Zimmermann 	.hotplug	= exynos_drm_fbdev_client_hotplug,
20099286486SThomas Zimmermann };
20149953b70SThomas Zimmermann 
exynos_drm_fbdev_setup(struct drm_device * dev)2021c248b7dSInki Dae void exynos_drm_fbdev_setup(struct drm_device *dev)
20349953b70SThomas Zimmermann {
2041c248b7dSInki Dae 	struct drm_fb_helper *fb_helper;
2051c248b7dSInki Dae 	int ret;
20649953b70SThomas Zimmermann 
20749953b70SThomas Zimmermann 	drm_WARN(dev, !dev->registered, "Device has not been registered.\n");
2081c248b7dSInki Dae 	drm_WARN(dev, dev->fb_helper, "fb_helper is already set!\n");
20949953b70SThomas Zimmermann 
2103bf3b534SThomas Zimmermann 	fb_helper = kzalloc(sizeof(*fb_helper), GFP_KERNEL);
2111c248b7dSInki Dae 	if (!fb_helper)
21249953b70SThomas Zimmermann 		return;
2131c248b7dSInki Dae 	drm_fb_helper_prepare(dev, fb_helper, PREFERRED_BPP, &exynos_drm_fb_helper_funcs);
21449953b70SThomas Zimmermann 
21549953b70SThomas Zimmermann 	ret = drm_client_init(dev, &fb_helper->client, "fbdev", &exynos_drm_fbdev_client_funcs);
21649953b70SThomas Zimmermann 	if (ret)
21749953b70SThomas Zimmermann 		goto err_drm_client_init;
21849953b70SThomas Zimmermann 
21949953b70SThomas Zimmermann 	drm_client_register(&fb_helper->client);
22049953b70SThomas Zimmermann 
22149953b70SThomas Zimmermann 	return;
22249953b70SThomas Zimmermann 
2233bf3b534SThomas Zimmermann err_drm_client_init:
2243bf3b534SThomas Zimmermann 	drm_fb_helper_unprepare(fb_helper);
2251c248b7dSInki Dae 	kfree(fb_helper);
226 }
227