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