Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
105struct drm_framebuffer *tegra_fb_alloc(struct drm_device *drm,
106 const struct drm_format_info *info,
107 const struct drm_mode_fb_cmd2 *mode_cmd,
108 struct tegra_bo **planes,
109 unsigned int num_planes)
110{
111 struct drm_framebuffer *fb;
112 unsigned int i;
113 int err;
114
115 fb = kzalloc(sizeof(*fb), GFP_KERNEL);
116 if (!fb)
117 return ERR_PTR(-ENOMEM);
118
119 drm_helper_mode_fill_fb_struct(drm, fb, info, mode_cmd);
120
121 for (i = 0; i < fb->format->num_planes; i++)
122 fb->obj[i] = &planes[i]->gem;
123
124 err = drm_framebuffer_init(drm, fb, &tegra_fb_funcs);
125 if (err < 0) {
126 dev_err(drm->dev, "failed to initialize framebuffer: %d\n",
127 err);
128 kfree(fb);
129 return ERR_PTR(err);
130 }
131
132 return fb;
133}
134
135struct drm_framebuffer *tegra_fb_create(struct drm_device *drm,
136 struct drm_file *file,
137 const struct drm_format_info *info,
138 const struct drm_mode_fb_cmd2 *cmd)
139{
140 struct tegra_bo *planes[4];
141 struct drm_gem_object *gem;
142 struct drm_framebuffer *fb;
143 unsigned int i;
144 int err;
145
146 for (i = 0; i < info->num_planes; i++) {
147 unsigned int width = cmd->width / (i ? info->hsub : 1);
148 unsigned int height = cmd->height / (i ? info->vsub : 1);
149 unsigned int size, bpp;
150
151 gem = drm_gem_object_lookup(file, cmd->handles[i]);
152 if (!gem) {
153 err = -ENXIO;
154 goto unreference;
155 }
156
157 bpp = info->cpp[i];
158
159 size = (height - 1) * cmd->pitches[i] +
160 width * bpp + cmd->offsets[i];
161
162 if (gem->size < size) {
163 err = -EINVAL;
164 drm_gem_object_put(gem);
165 goto unreference;
166 }
167
168 planes[i] = to_tegra_bo(gem);
169 }
170
171 fb = tegra_fb_alloc(drm, info, cmd, planes, i);
172 if (IS_ERR(fb)) {
173 err = PTR_ERR(fb);
174 goto unreference;
175 }
176
177 return fb;
178
179unreference:
180 while (i--)
181 drm_gem_object_put(&planes[i]->gem);
182
183 return ERR_PTR(err);
184}