xref: /linux/drivers/gpu/drm/tegra/fb.c (revision 815e260a18a3af4dab59025ee99a7156c0e8b5e0)
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 
21 struct 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 
27 bool 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 
37 int 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 
100 static const struct drm_framebuffer_funcs tegra_fb_funcs = {
101 	.destroy = drm_gem_fb_destroy,
102 	.create_handle = drm_gem_fb_create_handle,
103 };
104 
105 struct 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 
135 struct 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 
179 unreference:
180 	while (i--)
181 		drm_gem_object_put(&planes[i]->gem);
182 
183 	return ERR_PTR(err);
184 }
185