]> git.proxmox.com Git - mirror_ubuntu-focal-kernel.git/blame - drivers/gpu/host1x/drm/fb.c
drm/tegra: Cleanup tegra_dc structure
[mirror_ubuntu-focal-kernel.git] / drivers / gpu / host1x / drm / fb.c
CommitLineData
d8f4a9ed 1/*
de2ba664 2 * Copyright (C) 2012-2013 Avionic Design GmbH
d8f4a9ed
TR
3 * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved.
4 *
de2ba664
AM
5 * Based on the KMS/FB CMA helpers
6 * Copyright (C) 2012 Analog Device Inc.
7 *
d8f4a9ed
TR
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
de2ba664
AM
13#include <linux/module.h>
14
d8f4a9ed 15#include "drm.h"
de2ba664
AM
16#include "gem.h"
17
18static inline struct tegra_fb *to_tegra_fb(struct drm_framebuffer *fb)
19{
20 return container_of(fb, struct tegra_fb, base);
21}
22
23static inline struct tegra_fbdev *to_tegra_fbdev(struct drm_fb_helper *helper)
24{
25 return container_of(helper, struct tegra_fbdev, base);
26}
27
28struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer,
29 unsigned int index)
30{
31 struct tegra_fb *fb = to_tegra_fb(framebuffer);
32
33 if (index >= drm_format_num_planes(framebuffer->pixel_format))
34 return NULL;
35
36 return fb->planes[index];
37}
38
39static void tegra_fb_destroy(struct drm_framebuffer *framebuffer)
40{
41 struct tegra_fb *fb = to_tegra_fb(framebuffer);
42 unsigned int i;
43
44 for (i = 0; i < fb->num_planes; i++) {
45 struct tegra_bo *bo = fb->planes[i];
46
47 if (bo)
48 drm_gem_object_unreference_unlocked(&bo->gem);
49 }
50
51 drm_framebuffer_cleanup(framebuffer);
52 kfree(fb->planes);
53 kfree(fb);
54}
55
56static int tegra_fb_create_handle(struct drm_framebuffer *framebuffer,
57 struct drm_file *file, unsigned int *handle)
58{
59 struct tegra_fb *fb = to_tegra_fb(framebuffer);
60
61 return drm_gem_handle_create(file, &fb->planes[0]->gem, handle);
62}
63
64static struct drm_framebuffer_funcs tegra_fb_funcs = {
65 .destroy = tegra_fb_destroy,
66 .create_handle = tegra_fb_create_handle,
67};
68
69static struct tegra_fb *tegra_fb_alloc(struct drm_device *drm,
70 struct drm_mode_fb_cmd2 *mode_cmd,
71 struct tegra_bo **planes,
72 unsigned int num_planes)
73{
74 struct tegra_fb *fb;
75 unsigned int i;
76 int err;
77
78 fb = kzalloc(sizeof(*fb), GFP_KERNEL);
79 if (!fb)
80 return ERR_PTR(-ENOMEM);
81
82 fb->planes = kzalloc(num_planes * sizeof(*planes), GFP_KERNEL);
83 if (!fb->planes)
84 return ERR_PTR(-ENOMEM);
85
86 fb->num_planes = num_planes;
87
88 drm_helper_mode_fill_fb_struct(&fb->base, mode_cmd);
89
90 for (i = 0; i < fb->num_planes; i++)
91 fb->planes[i] = planes[i];
92
93 err = drm_framebuffer_init(drm, &fb->base, &tegra_fb_funcs);
94 if (err < 0) {
95 dev_err(drm->dev, "failed to initialize framebuffer: %d\n",
96 err);
97 kfree(fb->planes);
98 kfree(fb);
99 return ERR_PTR(err);
100 }
101
102 return fb;
103}
104
105static struct drm_framebuffer *tegra_fb_create(struct drm_device *drm,
106 struct drm_file *file,
107 struct drm_mode_fb_cmd2 *cmd)
108{
109 unsigned int hsub, vsub, i;
110 struct tegra_bo *planes[4];
111 struct drm_gem_object *gem;
112 struct tegra_fb *fb;
113 int err;
114
115 hsub = drm_format_horz_chroma_subsampling(cmd->pixel_format);
116 vsub = drm_format_vert_chroma_subsampling(cmd->pixel_format);
117
118 for (i = 0; i < drm_format_num_planes(cmd->pixel_format); i++) {
119 unsigned int width = cmd->width / (i ? hsub : 1);
120 unsigned int height = cmd->height / (i ? vsub : 1);
121 unsigned int size, bpp;
122
123 gem = drm_gem_object_lookup(drm, file, cmd->handles[i]);
124 if (!gem) {
125 err = -ENXIO;
126 goto unreference;
127 }
128
129 bpp = drm_format_plane_cpp(cmd->pixel_format, i);
130
131 size = (height - 1) * cmd->pitches[i] +
132 width * bpp + cmd->offsets[i];
133
134 if (gem->size < size) {
135 err = -EINVAL;
136 goto unreference;
137 }
138
139 planes[i] = to_tegra_bo(gem);
140 }
141
142 fb = tegra_fb_alloc(drm, cmd, planes, i);
143 if (IS_ERR(fb)) {
144 err = PTR_ERR(fb);
145 goto unreference;
146 }
147
148 return &fb->base;
149
150unreference:
151 while (i--)
152 drm_gem_object_unreference_unlocked(&planes[i]->gem);
d8f4a9ed 153
de2ba664
AM
154 return ERR_PTR(err);
155}
156
157static struct fb_ops tegra_fb_ops = {
158 .owner = THIS_MODULE,
159 .fb_fillrect = sys_fillrect,
160 .fb_copyarea = sys_copyarea,
161 .fb_imageblit = sys_imageblit,
162 .fb_check_var = drm_fb_helper_check_var,
163 .fb_set_par = drm_fb_helper_set_par,
164 .fb_blank = drm_fb_helper_blank,
165 .fb_pan_display = drm_fb_helper_pan_display,
166 .fb_setcmap = drm_fb_helper_setcmap,
167};
168
169static int tegra_fbdev_probe(struct drm_fb_helper *helper,
170 struct drm_fb_helper_surface_size *sizes)
171{
172 struct tegra_fbdev *fbdev = to_tegra_fbdev(helper);
173 struct drm_device *drm = helper->dev;
174 struct drm_mode_fb_cmd2 cmd = { 0 };
175 unsigned int bytes_per_pixel;
176 struct drm_framebuffer *fb;
177 unsigned long offset;
178 struct fb_info *info;
179 struct tegra_bo *bo;
180 size_t size;
181 int err;
182
183 bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8);
184
185 cmd.width = sizes->surface_width;
186 cmd.height = sizes->surface_height;
187 cmd.pitches[0] = sizes->surface_width * bytes_per_pixel;
188 cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
189 sizes->surface_depth);
190
191 size = cmd.pitches[0] * cmd.height;
192
193 bo = tegra_bo_create(drm, size);
194 if (IS_ERR(bo))
195 return PTR_ERR(bo);
196
197 info = framebuffer_alloc(0, drm->dev);
198 if (!info) {
199 dev_err(drm->dev, "failed to allocate framebuffer info\n");
200 tegra_bo_free_object(&bo->gem);
201 return -ENOMEM;
202 }
203
204 fbdev->fb = tegra_fb_alloc(drm, &cmd, &bo, 1);
205 if (IS_ERR(fbdev->fb)) {
206 dev_err(drm->dev, "failed to allocate DRM framebuffer\n");
207 err = PTR_ERR(fbdev->fb);
208 goto release;
209 }
210
211 fb = &fbdev->fb->base;
212 helper->fb = fb;
213 helper->fbdev = info;
214
215 info->par = helper;
216 info->flags = FBINFO_FLAG_DEFAULT;
217 info->fbops = &tegra_fb_ops;
218
219 err = fb_alloc_cmap(&info->cmap, 256, 0);
220 if (err < 0) {
221 dev_err(drm->dev, "failed to allocate color map: %d\n", err);
222 goto destroy;
223 }
224
225 drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
226 drm_fb_helper_fill_var(info, helper, fb->width, fb->height);
227
228 offset = info->var.xoffset * bytes_per_pixel +
229 info->var.yoffset * fb->pitches[0];
230
231 drm->mode_config.fb_base = (resource_size_t)bo->paddr;
232 info->screen_base = bo->vaddr + offset;
233 info->screen_size = size;
234 info->fix.smem_start = (unsigned long)(bo->paddr + offset);
235 info->fix.smem_len = size;
236
237 return 0;
238
239destroy:
240 drm_framebuffer_unregister_private(fb);
241 tegra_fb_destroy(fb);
242release:
243 framebuffer_release(info);
244 return err;
245}
246
247static struct drm_fb_helper_funcs tegra_fb_helper_funcs = {
248 .fb_probe = tegra_fbdev_probe,
249};
250
251static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm,
252 unsigned int preferred_bpp,
253 unsigned int num_crtc,
254 unsigned int max_connectors)
255{
256 struct drm_fb_helper *helper;
257 struct tegra_fbdev *fbdev;
258 int err;
259
260 fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
261 if (!fbdev) {
262 dev_err(drm->dev, "failed to allocate DRM fbdev\n");
263 return ERR_PTR(-ENOMEM);
264 }
265
266 fbdev->base.funcs = &tegra_fb_helper_funcs;
267 helper = &fbdev->base;
268
269 err = drm_fb_helper_init(drm, &fbdev->base, num_crtc, max_connectors);
270 if (err < 0) {
271 dev_err(drm->dev, "failed to initialize DRM FB helper\n");
272 goto free;
273 }
274
275 err = drm_fb_helper_single_add_all_connectors(&fbdev->base);
276 if (err < 0) {
277 dev_err(drm->dev, "failed to add connectors\n");
278 goto fini;
279 }
280
281 drm_helper_disable_unused_functions(drm);
282
283 err = drm_fb_helper_initial_config(&fbdev->base, preferred_bpp);
284 if (err < 0) {
285 dev_err(drm->dev, "failed to set initial configuration\n");
286 goto fini;
287 }
288
289 return fbdev;
290
291fini:
292 drm_fb_helper_fini(&fbdev->base);
293free:
294 kfree(fbdev);
295 return ERR_PTR(err);
296}
297
298static void tegra_fbdev_free(struct tegra_fbdev *fbdev)
299{
300 struct fb_info *info = fbdev->base.fbdev;
301
302 if (info) {
303 int err;
304
305 err = unregister_framebuffer(info);
306 if (err < 0)
307 DRM_DEBUG_KMS("failed to unregister framebuffer\n");
308
309 if (info->cmap.len)
310 fb_dealloc_cmap(&info->cmap);
311
312 framebuffer_release(info);
313 }
314
315 if (fbdev->fb) {
316 drm_framebuffer_unregister_private(&fbdev->fb->base);
317 tegra_fb_destroy(&fbdev->fb->base);
318 }
319
320 drm_fb_helper_fini(&fbdev->base);
321 kfree(fbdev);
322}
323
324static void tegra_fb_output_poll_changed(struct drm_device *drm)
d8f4a9ed 325{
c89c0ea6 326 struct host1x_drm *host1x = drm->dev_private;
d8f4a9ed 327
de2ba664
AM
328 if (host1x->fbdev)
329 drm_fb_helper_hotplug_event(&host1x->fbdev->base);
d8f4a9ed
TR
330}
331
332static const struct drm_mode_config_funcs tegra_drm_mode_funcs = {
de2ba664
AM
333 .fb_create = tegra_fb_create,
334 .output_poll_changed = tegra_fb_output_poll_changed,
d8f4a9ed
TR
335};
336
337int tegra_drm_fb_init(struct drm_device *drm)
338{
c89c0ea6 339 struct host1x_drm *host1x = drm->dev_private;
de2ba664 340 struct tegra_fbdev *fbdev;
d8f4a9ed
TR
341
342 drm->mode_config.min_width = 0;
343 drm->mode_config.min_height = 0;
344
345 drm->mode_config.max_width = 4096;
346 drm->mode_config.max_height = 4096;
347
348 drm->mode_config.funcs = &tegra_drm_mode_funcs;
349
de2ba664 350 fbdev = tegra_fbdev_create(drm, 32, drm->mode_config.num_crtc,
d8f4a9ed
TR
351 drm->mode_config.num_connector);
352 if (IS_ERR(fbdev))
353 return PTR_ERR(fbdev);
354
d8f4a9ed
TR
355 host1x->fbdev = fbdev;
356
357 return 0;
358}
359
360void tegra_drm_fb_exit(struct drm_device *drm)
361{
c89c0ea6 362 struct host1x_drm *host1x = drm->dev_private;
d8f4a9ed 363
de2ba664
AM
364 tegra_fbdev_free(host1x->fbdev);
365}
366
367void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev)
368{
369 if (fbdev) {
370 drm_modeset_lock_all(fbdev->base.dev);
371 drm_fb_helper_restore_fbdev_mode(&fbdev->base);
372 drm_modeset_unlock_all(fbdev->base.dev);
373 }
d8f4a9ed 374}