]> Git Repo - linux.git/blob - drivers/gpu/drm/exynos/exynos_drm_fbdev.c
Merge tag 'drm-next-2023-05-05' of git://anongit.freedesktop.org/drm/drm
[linux.git] / drivers / gpu / drm / exynos / exynos_drm_fbdev.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* exynos_drm_fbdev.c
3  *
4  * Copyright (c) 2011 Samsung Electronics Co., Ltd.
5  * Authors:
6  *      Inki Dae <[email protected]>
7  *      Joonyoung Shim <[email protected]>
8  *      Seung-Woo Kim <[email protected]>
9  */
10
11 #include <drm/drm_crtc_helper.h>
12 #include <drm/drm_drv.h>
13 #include <drm/drm_fb_helper.h>
14 #include <drm/drm_framebuffer.h>
15 #include <drm/drm_gem_framebuffer_helper.h>
16 #include <drm/drm_prime.h>
17 #include <drm/exynos_drm.h>
18
19 #include "exynos_drm_drv.h"
20 #include "exynos_drm_fb.h"
21 #include "exynos_drm_fbdev.h"
22
23 #define MAX_CONNECTOR           4
24 #define PREFERRED_BPP           32
25
26 static int exynos_drm_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
27 {
28         struct drm_fb_helper *helper = info->par;
29         struct drm_gem_object *obj = drm_gem_fb_get_obj(helper->fb, 0);
30
31         return drm_gem_prime_mmap(obj, vma);
32 }
33
34 static void exynos_drm_fb_destroy(struct fb_info *info)
35 {
36         struct drm_fb_helper *fb_helper = info->par;
37         struct drm_framebuffer *fb = fb_helper->fb;
38
39         drm_fb_helper_fini(fb_helper);
40
41         drm_framebuffer_remove(fb);
42
43         drm_client_release(&fb_helper->client);
44         drm_fb_helper_unprepare(fb_helper);
45         kfree(fb_helper);
46 }
47
48 static const struct fb_ops exynos_drm_fb_ops = {
49         .owner          = THIS_MODULE,
50         DRM_FB_HELPER_DEFAULT_OPS,
51         .fb_mmap        = exynos_drm_fb_mmap,
52         .fb_read        = drm_fb_helper_cfb_read,
53         .fb_write       = drm_fb_helper_cfb_write,
54         .fb_fillrect    = drm_fb_helper_cfb_fillrect,
55         .fb_copyarea    = drm_fb_helper_cfb_copyarea,
56         .fb_imageblit   = drm_fb_helper_cfb_imageblit,
57         .fb_destroy     = exynos_drm_fb_destroy,
58 };
59
60 static int exynos_drm_fbdev_update(struct drm_fb_helper *helper,
61                                    struct drm_fb_helper_surface_size *sizes,
62                                    struct exynos_drm_gem *exynos_gem)
63 {
64         struct fb_info *fbi;
65         struct drm_framebuffer *fb = helper->fb;
66         unsigned int size = fb->width * fb->height * fb->format->cpp[0];
67         unsigned long offset;
68
69         fbi = drm_fb_helper_alloc_info(helper);
70         if (IS_ERR(fbi)) {
71                 DRM_DEV_ERROR(to_dma_dev(helper->dev),
72                               "failed to allocate fb info.\n");
73                 return PTR_ERR(fbi);
74         }
75
76         fbi->fbops = &exynos_drm_fb_ops;
77
78         drm_fb_helper_fill_info(fbi, helper, sizes);
79
80         offset = fbi->var.xoffset * fb->format->cpp[0];
81         offset += fbi->var.yoffset * fb->pitches[0];
82
83         fbi->screen_buffer = exynos_gem->kvaddr + offset;
84         fbi->screen_size = size;
85         fbi->fix.smem_len = size;
86
87         return 0;
88 }
89
90 static int exynos_drm_fbdev_create(struct drm_fb_helper *helper,
91                                     struct drm_fb_helper_surface_size *sizes)
92 {
93         struct exynos_drm_gem *exynos_gem;
94         struct drm_device *dev = helper->dev;
95         struct drm_mode_fb_cmd2 mode_cmd = { 0 };
96         unsigned long size;
97         int ret;
98
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);
103
104         mode_cmd.width = sizes->surface_width;
105         mode_cmd.height = sizes->surface_height;
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);
109
110         size = mode_cmd.pitches[0] * mode_cmd.height;
111
112         exynos_gem = exynos_drm_gem_create(dev, EXYNOS_BO_WC, size, true);
113         if (IS_ERR(exynos_gem))
114                 return PTR_ERR(exynos_gem);
115
116         helper->fb =
117                 exynos_drm_framebuffer_init(dev, &mode_cmd, &exynos_gem, 1);
118         if (IS_ERR(helper->fb)) {
119                 DRM_DEV_ERROR(dev->dev, "failed to create drm framebuffer.\n");
120                 ret = PTR_ERR(helper->fb);
121                 goto err_destroy_gem;
122         }
123
124         ret = exynos_drm_fbdev_update(helper, sizes, exynos_gem);
125         if (ret < 0)
126                 goto err_destroy_framebuffer;
127
128         return 0;
129
130 err_destroy_framebuffer:
131         drm_framebuffer_cleanup(helper->fb);
132         helper->fb = NULL;
133 err_destroy_gem:
134         exynos_drm_gem_destroy(exynos_gem);
135         return ret;
136 }
137
138 static const struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
139         .fb_probe =     exynos_drm_fbdev_create,
140 };
141
142 /*
143  * struct drm_client
144  */
145
146 static void exynos_drm_fbdev_client_unregister(struct drm_client_dev *client)
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 }
158
159 static int exynos_drm_fbdev_client_restore(struct drm_client_dev *client)
160 {
161         drm_fb_helper_lastclose(client->dev);
162
163         return 0;
164 }
165
166 static int exynos_drm_fbdev_client_hotplug(struct drm_client_dev *client)
167 {
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
186         return 0;
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;
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
202 void exynos_drm_fbdev_setup(struct drm_device *dev)
203 {
204         struct drm_fb_helper *fb_helper;
205         int ret;
206
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");
209
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);
214
215         ret = drm_client_init(dev, &fb_helper->client, "fbdev", &exynos_drm_fbdev_client_funcs);
216         if (ret)
217                 goto err_drm_client_init;
218
219         ret = exynos_drm_fbdev_client_hotplug(&fb_helper->client);
220         if (ret)
221                 drm_dbg_kms(dev, "client hotplug ret=%d\n", ret);
222
223         drm_client_register(&fb_helper->client);
224
225         return;
226
227 err_drm_client_init:
228         drm_fb_helper_unprepare(fb_helper);
229         kfree(fb_helper);
230 }
This page took 0.043896 seconds and 4 git commands to generate.