Merge tag 'bcachefs-2024-02-17' of https://evilpiepirate.org/git/bcachefs
[linux-2.6-microblaze.git] / drivers / gpu / drm / tegra / fb.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2012-2013 Avionic Design GmbH
4  * Copyright (C) 2012 NVIDIA CORPORATION.  All rights reserved.
5  *
6  * Based on the KMS/FB DMA helpers
7  *   Copyright (C) 2012 Analog Devices Inc.
8  */
9
10 #include <linux/console.h>
11
12 #include <drm/drm_fourcc.h>
13 #include <drm/drm_framebuffer.h>
14 #include <drm/drm_gem_framebuffer_helper.h>
15 #include <drm/drm_modeset_helper.h>
16
17 #include "drm.h"
18 #include "gem.h"
19
20 struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer,
21                                     unsigned int index)
22 {
23         return to_tegra_bo(drm_gem_fb_get_obj(framebuffer, index));
24 }
25
26 bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer)
27 {
28         struct tegra_bo *bo = tegra_fb_get_plane(framebuffer, 0);
29
30         if (bo->flags & TEGRA_BO_BOTTOM_UP)
31                 return true;
32
33         return false;
34 }
35
36 int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer,
37                         struct tegra_bo_tiling *tiling)
38 {
39         uint64_t modifier = framebuffer->modifier;
40
41         if (fourcc_mod_is_vendor(modifier, NVIDIA)) {
42                 if ((modifier & DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT) == 0)
43                         tiling->sector_layout = TEGRA_BO_SECTOR_LAYOUT_TEGRA;
44                 else
45                         tiling->sector_layout = TEGRA_BO_SECTOR_LAYOUT_GPU;
46
47                 modifier &= ~DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT;
48         }
49
50         switch (modifier) {
51         case DRM_FORMAT_MOD_LINEAR:
52                 tiling->mode = TEGRA_BO_TILING_MODE_PITCH;
53                 tiling->value = 0;
54                 break;
55
56         case DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED:
57                 tiling->mode = TEGRA_BO_TILING_MODE_TILED;
58                 tiling->value = 0;
59                 break;
60
61         case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0):
62                 tiling->mode = TEGRA_BO_TILING_MODE_BLOCK;
63                 tiling->value = 0;
64                 break;
65
66         case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1):
67                 tiling->mode = TEGRA_BO_TILING_MODE_BLOCK;
68                 tiling->value = 1;
69                 break;
70
71         case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2):
72                 tiling->mode = TEGRA_BO_TILING_MODE_BLOCK;
73                 tiling->value = 2;
74                 break;
75
76         case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3):
77                 tiling->mode = TEGRA_BO_TILING_MODE_BLOCK;
78                 tiling->value = 3;
79                 break;
80
81         case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4):
82                 tiling->mode = TEGRA_BO_TILING_MODE_BLOCK;
83                 tiling->value = 4;
84                 break;
85
86         case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5):
87                 tiling->mode = TEGRA_BO_TILING_MODE_BLOCK;
88                 tiling->value = 5;
89                 break;
90
91         default:
92                 DRM_DEBUG_KMS("unknown format modifier: %llx\n", modifier);
93                 return -EINVAL;
94         }
95
96         return 0;
97 }
98
99 static const struct drm_framebuffer_funcs tegra_fb_funcs = {
100         .destroy = drm_gem_fb_destroy,
101         .create_handle = drm_gem_fb_create_handle,
102 };
103
104 struct drm_framebuffer *tegra_fb_alloc(struct drm_device *drm,
105                                        const struct drm_mode_fb_cmd2 *mode_cmd,
106                                        struct tegra_bo **planes,
107                                        unsigned int num_planes)
108 {
109         struct drm_framebuffer *fb;
110         unsigned int i;
111         int err;
112
113         fb = kzalloc(sizeof(*fb), GFP_KERNEL);
114         if (!fb)
115                 return ERR_PTR(-ENOMEM);
116
117         drm_helper_mode_fill_fb_struct(drm, fb, mode_cmd);
118
119         for (i = 0; i < fb->format->num_planes; i++)
120                 fb->obj[i] = &planes[i]->gem;
121
122         err = drm_framebuffer_init(drm, fb, &tegra_fb_funcs);
123         if (err < 0) {
124                 dev_err(drm->dev, "failed to initialize framebuffer: %d\n",
125                         err);
126                 kfree(fb);
127                 return ERR_PTR(err);
128         }
129
130         return fb;
131 }
132
133 struct drm_framebuffer *tegra_fb_create(struct drm_device *drm,
134                                         struct drm_file *file,
135                                         const struct drm_mode_fb_cmd2 *cmd)
136 {
137         const struct drm_format_info *info = drm_get_format_info(drm, cmd);
138         struct tegra_bo *planes[4];
139         struct drm_gem_object *gem;
140         struct drm_framebuffer *fb;
141         unsigned int i;
142         int err;
143
144         for (i = 0; i < info->num_planes; i++) {
145                 unsigned int width = cmd->width / (i ? info->hsub : 1);
146                 unsigned int height = cmd->height / (i ? info->vsub : 1);
147                 unsigned int size, bpp;
148
149                 gem = drm_gem_object_lookup(file, cmd->handles[i]);
150                 if (!gem) {
151                         err = -ENXIO;
152                         goto unreference;
153                 }
154
155                 bpp = info->cpp[i];
156
157                 size = (height - 1) * cmd->pitches[i] +
158                        width * bpp + cmd->offsets[i];
159
160                 if (gem->size < size) {
161                         err = -EINVAL;
162                         goto unreference;
163                 }
164
165                 planes[i] = to_tegra_bo(gem);
166         }
167
168         fb = tegra_fb_alloc(drm, cmd, planes, i);
169         if (IS_ERR(fb)) {
170                 err = PTR_ERR(fb);
171                 goto unreference;
172         }
173
174         return fb;
175
176 unreference:
177         while (i--)
178                 drm_gem_object_put(&planes[i]->gem);
179
180         return ERR_PTR(err);
181 }