Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.c
51984 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* (C) COPYRIGHT 2018 ARM Limited. All rights reserved.
4
* Author: James.Qian.Wang <[email protected]>
5
*
6
*/
7
#include <drm/drm_device.h>
8
#include <drm/drm_fb_dma_helper.h>
9
#include <drm/drm_gem.h>
10
#include <drm/drm_gem_dma_helper.h>
11
#include <drm/drm_gem_framebuffer_helper.h>
12
#include <drm/drm_print.h>
13
14
#include "komeda_framebuffer.h"
15
#include "komeda_dev.h"
16
17
static void komeda_fb_destroy(struct drm_framebuffer *fb)
18
{
19
struct komeda_fb *kfb = to_kfb(fb);
20
u32 i;
21
22
for (i = 0; i < fb->format->num_planes; i++)
23
drm_gem_object_put(fb->obj[i]);
24
25
drm_framebuffer_cleanup(fb);
26
kfree(kfb);
27
}
28
29
static int komeda_fb_create_handle(struct drm_framebuffer *fb,
30
struct drm_file *file, u32 *handle)
31
{
32
return drm_gem_handle_create(file, fb->obj[0], handle);
33
}
34
35
static const struct drm_framebuffer_funcs komeda_fb_funcs = {
36
.destroy = komeda_fb_destroy,
37
.create_handle = komeda_fb_create_handle,
38
};
39
40
static int
41
komeda_fb_afbc_size_check(struct komeda_fb *kfb, struct drm_file *file,
42
const struct drm_mode_fb_cmd2 *mode_cmd)
43
{
44
struct drm_framebuffer *fb = &kfb->base;
45
const struct drm_format_info *info = fb->format;
46
struct drm_gem_object *obj;
47
u32 alignment_w = 0, alignment_h = 0, alignment_header, n_blocks, bpp;
48
u64 min_size;
49
50
obj = drm_gem_object_lookup(file, mode_cmd->handles[0]);
51
if (!obj) {
52
DRM_DEBUG_KMS("Failed to lookup GEM object\n");
53
return -ENOENT;
54
}
55
56
switch (fb->modifier & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK) {
57
case AFBC_FORMAT_MOD_BLOCK_SIZE_32x8:
58
alignment_w = 32;
59
alignment_h = 8;
60
break;
61
case AFBC_FORMAT_MOD_BLOCK_SIZE_16x16:
62
alignment_w = 16;
63
alignment_h = 16;
64
break;
65
default:
66
WARN(1, "Invalid AFBC_FORMAT_MOD_BLOCK_SIZE: %lld.\n",
67
fb->modifier & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK);
68
break;
69
}
70
71
/* tiled header afbc */
72
if (fb->modifier & AFBC_FORMAT_MOD_TILED) {
73
alignment_w *= AFBC_TH_LAYOUT_ALIGNMENT;
74
alignment_h *= AFBC_TH_LAYOUT_ALIGNMENT;
75
alignment_header = AFBC_TH_BODY_START_ALIGNMENT;
76
} else {
77
alignment_header = AFBC_BODY_START_ALIGNMENT;
78
}
79
80
kfb->aligned_w = ALIGN(fb->width, alignment_w);
81
kfb->aligned_h = ALIGN(fb->height, alignment_h);
82
83
if (fb->offsets[0] % alignment_header) {
84
DRM_DEBUG_KMS("afbc offset alignment check failed.\n");
85
goto check_failed;
86
}
87
88
n_blocks = (kfb->aligned_w * kfb->aligned_h) / AFBC_SUPERBLK_PIXELS;
89
kfb->offset_payload = ALIGN(n_blocks * AFBC_HEADER_SIZE,
90
alignment_header);
91
92
bpp = komeda_get_afbc_format_bpp(info, fb->modifier);
93
kfb->afbc_size = kfb->offset_payload + n_blocks *
94
ALIGN(bpp * AFBC_SUPERBLK_PIXELS / 8,
95
AFBC_SUPERBLK_ALIGNMENT);
96
min_size = kfb->afbc_size + fb->offsets[0];
97
if (min_size > obj->size) {
98
DRM_DEBUG_KMS("afbc size check failed, obj_size: 0x%zx. min_size 0x%llx.\n",
99
obj->size, min_size);
100
goto check_failed;
101
}
102
103
fb->obj[0] = obj;
104
return 0;
105
106
check_failed:
107
drm_gem_object_put(obj);
108
return -EINVAL;
109
}
110
111
static int
112
komeda_fb_none_afbc_size_check(struct komeda_dev *mdev, struct komeda_fb *kfb,
113
struct drm_file *file,
114
const struct drm_mode_fb_cmd2 *mode_cmd)
115
{
116
struct drm_framebuffer *fb = &kfb->base;
117
const struct drm_format_info *info = fb->format;
118
struct drm_gem_object *obj;
119
u32 i, block_h;
120
u64 min_size;
121
122
if (komeda_fb_check_src_coords(kfb, 0, 0, fb->width, fb->height))
123
return -EINVAL;
124
125
for (i = 0; i < info->num_planes; i++) {
126
obj = drm_gem_object_lookup(file, mode_cmd->handles[i]);
127
if (!obj) {
128
DRM_DEBUG_KMS("Failed to lookup GEM object\n");
129
return -ENOENT;
130
}
131
fb->obj[i] = obj;
132
133
block_h = drm_format_info_block_height(info, i);
134
if ((fb->pitches[i] * block_h) % mdev->chip.bus_width) {
135
DRM_DEBUG_KMS("Pitch[%d]: 0x%x doesn't align to 0x%x\n",
136
i, fb->pitches[i], mdev->chip.bus_width);
137
return -EINVAL;
138
}
139
140
min_size = komeda_fb_get_pixel_addr(kfb, 0, fb->height, i)
141
- to_drm_gem_dma_obj(obj)->dma_addr;
142
if (obj->size < min_size) {
143
DRM_DEBUG_KMS("The fb->obj[%d] size: 0x%zx lower than the minimum requirement: 0x%llx.\n",
144
i, obj->size, min_size);
145
return -EINVAL;
146
}
147
}
148
149
if (fb->format->num_planes == 3) {
150
if (fb->pitches[1] != fb->pitches[2]) {
151
DRM_DEBUG_KMS("The pitch[1] and [2] are not same\n");
152
return -EINVAL;
153
}
154
}
155
156
return 0;
157
}
158
159
struct drm_framebuffer *
160
komeda_fb_create(struct drm_device *dev, struct drm_file *file,
161
const struct drm_format_info *info,
162
const struct drm_mode_fb_cmd2 *mode_cmd)
163
{
164
struct komeda_dev *mdev = dev->dev_private;
165
struct komeda_fb *kfb;
166
int ret = 0, i;
167
168
kfb = kzalloc(sizeof(*kfb), GFP_KERNEL);
169
if (!kfb)
170
return ERR_PTR(-ENOMEM);
171
172
kfb->format_caps = komeda_get_format_caps(&mdev->fmt_tbl,
173
mode_cmd->pixel_format,
174
mode_cmd->modifier[0]);
175
if (!kfb->format_caps) {
176
DRM_DEBUG_KMS("FMT %x is not supported.\n",
177
mode_cmd->pixel_format);
178
kfree(kfb);
179
return ERR_PTR(-EINVAL);
180
}
181
182
drm_helper_mode_fill_fb_struct(dev, &kfb->base, info, mode_cmd);
183
184
if (kfb->base.modifier)
185
ret = komeda_fb_afbc_size_check(kfb, file, mode_cmd);
186
else
187
ret = komeda_fb_none_afbc_size_check(mdev, kfb, file, mode_cmd);
188
if (ret < 0)
189
goto err_cleanup;
190
191
ret = drm_framebuffer_init(dev, &kfb->base, &komeda_fb_funcs);
192
if (ret < 0) {
193
DRM_DEBUG_KMS("failed to initialize fb\n");
194
195
goto err_cleanup;
196
}
197
198
kfb->is_va = mdev->iommu ? true : false;
199
200
return &kfb->base;
201
202
err_cleanup:
203
for (i = 0; i < kfb->base.format->num_planes; i++)
204
drm_gem_object_put(kfb->base.obj[i]);
205
206
kfree(kfb);
207
return ERR_PTR(ret);
208
}
209
210
int komeda_fb_check_src_coords(const struct komeda_fb *kfb,
211
u32 src_x, u32 src_y, u32 src_w, u32 src_h)
212
{
213
const struct drm_framebuffer *fb = &kfb->base;
214
const struct drm_format_info *info = fb->format;
215
u32 block_w = drm_format_info_block_width(fb->format, 0);
216
u32 block_h = drm_format_info_block_height(fb->format, 0);
217
218
if ((src_x + src_w > fb->width) || (src_y + src_h > fb->height)) {
219
DRM_DEBUG_ATOMIC("Invalid source coordinate.\n");
220
return -EINVAL;
221
}
222
223
if ((src_x % info->hsub) || (src_w % info->hsub) ||
224
(src_y % info->vsub) || (src_h % info->vsub)) {
225
DRM_DEBUG_ATOMIC("Wrong subsampling dimension x:%d, y:%d, w:%d, h:%d for format: %x.\n",
226
src_x, src_y, src_w, src_h, info->format);
227
return -EINVAL;
228
}
229
230
if ((src_x % block_w) || (src_w % block_w) ||
231
(src_y % block_h) || (src_h % block_h)) {
232
DRM_DEBUG_ATOMIC("x:%d, y:%d, w:%d, h:%d should be multiple of block_w/h for format: %x.\n",
233
src_x, src_y, src_w, src_h, info->format);
234
return -EINVAL;
235
}
236
237
return 0;
238
}
239
240
dma_addr_t
241
komeda_fb_get_pixel_addr(struct komeda_fb *kfb, int x, int y, int plane)
242
{
243
struct drm_framebuffer *fb = &kfb->base;
244
const struct drm_gem_dma_object *obj;
245
u32 offset, plane_x, plane_y, block_w, block_sz;
246
247
if (plane >= fb->format->num_planes) {
248
DRM_DEBUG_KMS("Out of max plane num.\n");
249
return -EINVAL;
250
}
251
252
obj = drm_fb_dma_get_gem_obj(fb, plane);
253
254
offset = fb->offsets[plane];
255
if (!fb->modifier) {
256
block_w = drm_format_info_block_width(fb->format, plane);
257
block_sz = fb->format->char_per_block[plane];
258
plane_x = x / (plane ? fb->format->hsub : 1);
259
plane_y = y / (plane ? fb->format->vsub : 1);
260
261
offset += (plane_x / block_w) * block_sz
262
+ plane_y * fb->pitches[plane];
263
}
264
265
return obj->dma_addr + offset;
266
}
267
268
/* if the fb can be supported by a specific layer */
269
bool komeda_fb_is_layer_supported(struct komeda_fb *kfb, u32 layer_type,
270
u32 rot)
271
{
272
struct drm_framebuffer *fb = &kfb->base;
273
struct komeda_dev *mdev = fb->dev->dev_private;
274
u32 fourcc = fb->format->format;
275
u64 modifier = fb->modifier;
276
bool supported;
277
278
supported = komeda_format_mod_supported(&mdev->fmt_tbl, layer_type,
279
fourcc, modifier, rot);
280
if (!supported)
281
DRM_DEBUG_ATOMIC("Layer TYPE: %d doesn't support fb FMT: %p4cc with modifier: 0x%llx.\n",
282
layer_type, &fourcc, modifier);
283
284
return supported;
285
}
286
287