projects/linux·blob master · refs

185 lines · 4.1 KiB

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