xref: /linux/drivers/gpu/drm/tegra/fb.c (revision 4b99990cdf9560e8a071640baf19f312e6ae02f4)
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 static
106 struct 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 
136 struct 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 
180 unreference:
181 	while (i--)
182 		drm_gem_object_put(&planes[i]->gem);
183 
184 	return ERR_PTR(err);
185 }
186