Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
1c248b7d ID |
2 | /* exynos_drm_fbdev.c |
3 | * | |
4 | * Copyright (c) 2011 Samsung Electronics Co., Ltd. | |
5 | * Authors: | |
6 | * Inki Dae <inki.dae@samsung.com> | |
7 | * Joonyoung Shim <jy0922.shim@samsung.com> | |
8 | * Seung-Woo Kim <sw0312.kim@samsung.com> | |
1c248b7d ID |
9 | */ |
10 | ||
ac9dc1b1 TZ |
11 | #include <linux/fb.h> |
12 | ||
49953b70 TZ |
13 | #include <drm/drm_crtc_helper.h> |
14 | #include <drm/drm_drv.h> | |
760285e7 | 15 | #include <drm/drm_fb_helper.h> |
720cf96d | 16 | #include <drm/drm_framebuffer.h> |
89c258b5 | 17 | #include <drm/drm_gem_framebuffer_helper.h> |
2043e6f6 | 18 | #include <drm/drm_prime.h> |
a1bfacf4 | 19 | #include <drm/exynos_drm.h> |
1c248b7d ID |
20 | |
21 | #include "exynos_drm_drv.h" | |
22 | #include "exynos_drm_fb.h" | |
e30655d0 | 23 | #include "exynos_drm_fbdev.h" |
1c248b7d ID |
24 | |
25 | #define MAX_CONNECTOR 4 | |
26 | #define PREFERRED_BPP 32 | |
27 | ||
89c258b5 | 28 | static int exynos_drm_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) |
dd265850 P |
29 | { |
30 | struct drm_fb_helper *helper = info->par; | |
89c258b5 | 31 | struct drm_gem_object *obj = drm_gem_fb_get_obj(helper->fb, 0); |
dd265850 | 32 | |
89c258b5 | 33 | return drm_gem_prime_mmap(obj, vma); |
dd265850 P |
34 | } |
35 | ||
49953b70 TZ |
36 | static void exynos_drm_fb_destroy(struct fb_info *info) |
37 | { | |
38 | struct drm_fb_helper *fb_helper = info->par; | |
39 | struct drm_framebuffer *fb = fb_helper->fb; | |
40 | ||
41 | drm_fb_helper_fini(fb_helper); | |
42 | ||
43 | drm_framebuffer_remove(fb); | |
44 | ||
45 | drm_client_release(&fb_helper->client); | |
46 | drm_fb_helper_unprepare(fb_helper); | |
47 | kfree(fb_helper); | |
48 | } | |
49 | ||
b6ff753a | 50 | static const struct fb_ops exynos_drm_fb_ops = { |
1c248b7d | 51 | .owner = THIS_MODULE, |
b21f187f | 52 | __FB_DEFAULT_DMAMEM_OPS_RDWR, |
2eec838c | 53 | DRM_FB_HELPER_DEFAULT_OPS, |
b21f187f | 54 | __FB_DEFAULT_DMAMEM_OPS_DRAW, |
dd265850 | 55 | .fb_mmap = exynos_drm_fb_mmap, |
49953b70 | 56 | .fb_destroy = exynos_drm_fb_destroy, |
1c248b7d ID |
57 | }; |
58 | ||
19c8b834 | 59 | static int exynos_drm_fbdev_update(struct drm_fb_helper *helper, |
d7619960 | 60 | struct drm_fb_helper_surface_size *sizes, |
813fd67b | 61 | struct exynos_drm_gem *exynos_gem) |
1c248b7d | 62 | { |
ee885ca5 | 63 | struct fb_info *fbi; |
d7619960 | 64 | struct drm_framebuffer *fb = helper->fb; |
272725c7 | 65 | unsigned int size = fb->width * fb->height * fb->format->cpp[0]; |
19c8b834 | 66 | unsigned long offset; |
1c248b7d | 67 | |
7fd50bc3 | 68 | fbi = drm_fb_helper_alloc_info(helper); |
ee885ca5 | 69 | if (IS_ERR(fbi)) { |
6f83d208 ID |
70 | DRM_DEV_ERROR(to_dma_dev(helper->dev), |
71 | "failed to allocate fb info.\n"); | |
ee885ca5 JS |
72 | return PTR_ERR(fbi); |
73 | } | |
74 | ||
ee885ca5 JS |
75 | fbi->fbops = &exynos_drm_fb_ops; |
76 | ||
fb68e596 | 77 | drm_fb_helper_fill_info(fbi, helper, sizes); |
1c248b7d | 78 | |
272725c7 | 79 | offset = fbi->var.xoffset * fb->format->cpp[0]; |
01f2c773 | 80 | offset += fbi->var.yoffset * fb->pitches[0]; |
1c248b7d | 81 | |
5ad315c8 | 82 | fbi->flags |= FBINFO_VIRTFB; |
d4035d10 | 83 | fbi->screen_buffer = exynos_gem->kvaddr + offset; |
1c248b7d | 84 | fbi->screen_size = size; |
71b1f195 | 85 | fbi->fix.smem_len = size; |
19c8b834 ID |
86 | |
87 | return 0; | |
1c248b7d ID |
88 | } |
89 | ||
90 | static int exynos_drm_fbdev_create(struct drm_fb_helper *helper, | |
91 | struct drm_fb_helper_surface_size *sizes) | |
92 | { | |
813fd67b | 93 | struct exynos_drm_gem *exynos_gem; |
1c248b7d | 94 | struct drm_device *dev = helper->dev; |
a794d57d | 95 | struct drm_mode_fb_cmd2 mode_cmd = { 0 }; |
e1533c08 | 96 | unsigned long size; |
1c248b7d ID |
97 | int ret; |
98 | ||
6be90056 ID |
99 | DRM_DEV_DEBUG_KMS(dev->dev, |
100 | "surface width(%d), height(%d) and bpp(%d\n", | |
101 | sizes->surface_width, sizes->surface_height, | |
102 | sizes->surface_bpp); | |
1c248b7d ID |
103 | |
104 | mode_cmd.width = sizes->surface_width; | |
105 | mode_cmd.height = sizes->surface_height; | |
a794d57d JS |
106 | mode_cmd.pitches[0] = sizes->surface_width * (sizes->surface_bpp >> 3); |
107 | mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, | |
108 | sizes->surface_depth); | |
1c248b7d | 109 | |
e1533c08 | 110 | size = mode_cmd.pitches[0] * mode_cmd.height; |
2b35892e | 111 | |
9940d9d9 | 112 | exynos_gem = exynos_drm_gem_create(dev, EXYNOS_BO_WC, size, true); |
2f424200 DV |
113 | if (IS_ERR(exynos_gem)) |
114 | return PTR_ERR(exynos_gem); | |
e1533c08 | 115 | |
813fd67b JS |
116 | helper->fb = |
117 | exynos_drm_framebuffer_init(dev, &mode_cmd, &exynos_gem, 1); | |
41eab402 | 118 | if (IS_ERR(helper->fb)) { |
6f83d208 | 119 | DRM_DEV_ERROR(dev->dev, "failed to create drm framebuffer.\n"); |
e1533c08 | 120 | ret = PTR_ERR(helper->fb); |
662aa6d7 | 121 | goto err_destroy_gem; |
1c248b7d ID |
122 | } |
123 | ||
813fd67b | 124 | ret = exynos_drm_fbdev_update(helper, sizes, exynos_gem); |
662aa6d7 | 125 | if (ret < 0) |
7c7d4507 | 126 | goto err_destroy_framebuffer; |
662aa6d7 | 127 | |
49953b70 | 128 | return 0; |
662aa6d7 | 129 | |
662aa6d7 ID |
130 | err_destroy_framebuffer: |
131 | drm_framebuffer_cleanup(helper->fb); | |
49953b70 | 132 | helper->fb = NULL; |
662aa6d7 | 133 | err_destroy_gem: |
813fd67b | 134 | exynos_drm_gem_destroy(exynos_gem); |
1c248b7d ID |
135 | return ret; |
136 | } | |
137 | ||
3a493879 | 138 | static const struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = { |
cd5428a5 | 139 | .fb_probe = exynos_drm_fbdev_create, |
1c248b7d ID |
140 | }; |
141 | ||
99286486 TZ |
142 | /* |
143 | * struct drm_client | |
144 | */ | |
145 | ||
146 | static void exynos_drm_fbdev_client_unregister(struct drm_client_dev *client) | |
49953b70 TZ |
147 | { |
148 | struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client); | |
149 | ||
150 | if (fb_helper->info) { | |
151 | drm_fb_helper_unregister_info(fb_helper); | |
152 | } else { | |
153 | drm_client_release(&fb_helper->client); | |
154 | drm_fb_helper_unprepare(fb_helper); | |
155 | kfree(fb_helper); | |
156 | } | |
157 | } | |
99286486 TZ |
158 | |
159 | static int exynos_drm_fbdev_client_restore(struct drm_client_dev *client) | |
160 | { | |
49953b70 TZ |
161 | drm_fb_helper_lastclose(client->dev); |
162 | ||
99286486 TZ |
163 | return 0; |
164 | } | |
165 | ||
166 | static int exynos_drm_fbdev_client_hotplug(struct drm_client_dev *client) | |
167 | { | |
49953b70 TZ |
168 | struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client); |
169 | struct drm_device *dev = client->dev; | |
170 | int ret; | |
171 | ||
172 | if (dev->fb_helper) | |
173 | return drm_fb_helper_hotplug_event(dev->fb_helper); | |
174 | ||
175 | ret = drm_fb_helper_init(dev, fb_helper); | |
176 | if (ret) | |
177 | goto err_drm_err; | |
178 | ||
179 | if (!drm_drv_uses_atomic_modeset(dev)) | |
180 | drm_helper_disable_unused_functions(dev); | |
181 | ||
182 | ret = drm_fb_helper_initial_config(fb_helper); | |
183 | if (ret) | |
184 | goto err_drm_fb_helper_fini; | |
185 | ||
99286486 | 186 | return 0; |
49953b70 TZ |
187 | |
188 | err_drm_fb_helper_fini: | |
189 | drm_fb_helper_fini(fb_helper); | |
190 | err_drm_err: | |
191 | drm_err(dev, "Failed to setup fbdev emulation (ret=%d)\n", ret); | |
192 | return ret; | |
99286486 TZ |
193 | } |
194 | ||
195 | static const struct drm_client_funcs exynos_drm_fbdev_client_funcs = { | |
196 | .owner = THIS_MODULE, | |
197 | .unregister = exynos_drm_fbdev_client_unregister, | |
198 | .restore = exynos_drm_fbdev_client_restore, | |
199 | .hotplug = exynos_drm_fbdev_client_hotplug, | |
200 | }; | |
201 | ||
49953b70 | 202 | void exynos_drm_fbdev_setup(struct drm_device *dev) |
1c248b7d | 203 | { |
49953b70 | 204 | struct drm_fb_helper *fb_helper; |
1c248b7d ID |
205 | int ret; |
206 | ||
49953b70 TZ |
207 | drm_WARN(dev, !dev->registered, "Device has not been registered.\n"); |
208 | drm_WARN(dev, dev->fb_helper, "fb_helper is already set!\n"); | |
1c248b7d | 209 | |
49953b70 TZ |
210 | fb_helper = kzalloc(sizeof(*fb_helper), GFP_KERNEL); |
211 | if (!fb_helper) | |
212 | return; | |
213 | drm_fb_helper_prepare(dev, fb_helper, PREFERRED_BPP, &exynos_drm_fb_helper_funcs); | |
1c248b7d | 214 | |
49953b70 | 215 | ret = drm_client_init(dev, &fb_helper->client, "fbdev", &exynos_drm_fbdev_client_funcs); |
99286486 | 216 | if (ret) |
49953b70 | 217 | goto err_drm_client_init; |
1c248b7d | 218 | |
49953b70 | 219 | drm_client_register(&fb_helper->client); |
1c248b7d | 220 | |
49953b70 | 221 | return; |
1c248b7d | 222 | |
49953b70 | 223 | err_drm_client_init: |
3bf3b534 TZ |
224 | drm_fb_helper_unprepare(fb_helper); |
225 | kfree(fb_helper); | |
1c248b7d | 226 | } |