1 // SPDX-License-Identifier: GPL-2.0
3 * (C) COPYRIGHT 2018 ARM Limited. All rights reserved.
4 * Author: James.Qian.Wang <james.qian.wang@arm.com>
7 #include <drm/drm_device.h>
8 #include <drm/drm_fb_cma_helper.h>
9 #include <drm/drm_gem.h>
10 #include <drm/drm_gem_cma_helper.h>
11 #include <drm/drm_gem_framebuffer_helper.h>
13 #include "komeda_framebuffer.h"
14 #include "komeda_dev.h"
16 static void komeda_fb_destroy(struct drm_framebuffer
*fb
)
18 struct komeda_fb
*kfb
= to_kfb(fb
);
21 for (i
= 0; i
< fb
->format
->num_planes
; i
++)
22 drm_gem_object_put_unlocked(fb
->obj
[i
]);
24 drm_framebuffer_cleanup(fb
);
28 static int komeda_fb_create_handle(struct drm_framebuffer
*fb
,
29 struct drm_file
*file
, u32
*handle
)
31 return drm_gem_handle_create(file
, fb
->obj
[0], handle
);
34 static const struct drm_framebuffer_funcs komeda_fb_funcs
= {
35 .destroy
= komeda_fb_destroy
,
36 .create_handle
= komeda_fb_create_handle
,
40 komeda_fb_afbc_size_check(struct komeda_fb
*kfb
, struct drm_file
*file
,
41 const struct drm_mode_fb_cmd2
*mode_cmd
)
43 struct drm_framebuffer
*fb
= &kfb
->base
;
44 const struct drm_format_info
*info
= fb
->format
;
45 struct drm_gem_object
*obj
;
46 u32 alignment_w
= 0, alignment_h
= 0, alignment_header
, n_blocks
, bpp
;
49 obj
= drm_gem_object_lookup(file
, mode_cmd
->handles
[0]);
51 DRM_DEBUG_KMS("Failed to lookup GEM object\n");
55 switch (fb
->modifier
& AFBC_FORMAT_MOD_BLOCK_SIZE_MASK
) {
56 case AFBC_FORMAT_MOD_BLOCK_SIZE_32x8
:
60 case AFBC_FORMAT_MOD_BLOCK_SIZE_16x16
:
65 WARN(1, "Invalid AFBC_FORMAT_MOD_BLOCK_SIZE: %lld.\n",
66 fb
->modifier
& AFBC_FORMAT_MOD_BLOCK_SIZE_MASK
);
70 /* tiled header afbc */
71 if (fb
->modifier
& AFBC_FORMAT_MOD_TILED
) {
72 alignment_w
*= AFBC_TH_LAYOUT_ALIGNMENT
;
73 alignment_h
*= AFBC_TH_LAYOUT_ALIGNMENT
;
74 alignment_header
= AFBC_TH_BODY_START_ALIGNMENT
;
76 alignment_header
= AFBC_BODY_START_ALIGNMENT
;
79 kfb
->aligned_w
= ALIGN(fb
->width
, alignment_w
);
80 kfb
->aligned_h
= ALIGN(fb
->height
, alignment_h
);
82 if (fb
->offsets
[0] % alignment_header
) {
83 DRM_DEBUG_KMS("afbc offset alignment check failed.\n");
87 n_blocks
= (kfb
->aligned_w
* kfb
->aligned_h
) / AFBC_SUPERBLK_PIXELS
;
88 kfb
->offset_payload
= ALIGN(n_blocks
* AFBC_HEADER_SIZE
,
91 bpp
= komeda_get_afbc_format_bpp(info
, fb
->modifier
);
92 kfb
->afbc_size
= kfb
->offset_payload
+ n_blocks
*
93 ALIGN(bpp
* AFBC_SUPERBLK_PIXELS
/ 8,
94 AFBC_SUPERBLK_ALIGNMENT
);
95 min_size
= kfb
->afbc_size
+ fb
->offsets
[0];
96 if (min_size
> obj
->size
) {
97 DRM_DEBUG_KMS("afbc size check failed, obj_size: 0x%zx. min_size 0x%llx.\n",
106 drm_gem_object_put_unlocked(obj
);
111 komeda_fb_none_afbc_size_check(struct komeda_dev
*mdev
, struct komeda_fb
*kfb
,
112 struct drm_file
*file
,
113 const struct drm_mode_fb_cmd2
*mode_cmd
)
115 struct drm_framebuffer
*fb
= &kfb
->base
;
116 const struct drm_format_info
*info
= fb
->format
;
117 struct drm_gem_object
*obj
;
121 if (komeda_fb_check_src_coords(kfb
, 0, 0, fb
->width
, fb
->height
))
124 for (i
= 0; i
< info
->num_planes
; i
++) {
125 obj
= drm_gem_object_lookup(file
, mode_cmd
->handles
[i
]);
127 DRM_DEBUG_KMS("Failed to lookup GEM object\n");
132 block_h
= drm_format_info_block_height(info
, i
);
133 if ((fb
->pitches
[i
] * block_h
) % mdev
->chip
.bus_width
) {
134 DRM_DEBUG_KMS("Pitch[%d]: 0x%x doesn't align to 0x%x\n",
135 i
, fb
->pitches
[i
], mdev
->chip
.bus_width
);
139 min_size
= komeda_fb_get_pixel_addr(kfb
, 0, fb
->height
, i
)
140 - to_drm_gem_cma_obj(obj
)->paddr
;
141 if (obj
->size
< min_size
) {
142 DRM_DEBUG_KMS("The fb->obj[%d] size: 0x%zx lower than the minimum requirement: 0x%llx.\n",
143 i
, obj
->size
, min_size
);
148 if (fb
->format
->num_planes
== 3) {
149 if (fb
->pitches
[1] != fb
->pitches
[2]) {
150 DRM_DEBUG_KMS("The pitch[1] and [2] are not same\n");
158 struct drm_framebuffer
*
159 komeda_fb_create(struct drm_device
*dev
, struct drm_file
*file
,
160 const struct drm_mode_fb_cmd2
*mode_cmd
)
162 struct komeda_dev
*mdev
= dev
->dev_private
;
163 struct komeda_fb
*kfb
;
166 kfb
= kzalloc(sizeof(*kfb
), GFP_KERNEL
);
168 return ERR_PTR(-ENOMEM
);
170 kfb
->format_caps
= komeda_get_format_caps(&mdev
->fmt_tbl
,
171 mode_cmd
->pixel_format
,
172 mode_cmd
->modifier
[0]);
173 if (!kfb
->format_caps
) {
174 DRM_DEBUG_KMS("FMT %x is not supported.\n",
175 mode_cmd
->pixel_format
);
177 return ERR_PTR(-EINVAL
);
180 drm_helper_mode_fill_fb_struct(dev
, &kfb
->base
, mode_cmd
);
182 if (kfb
->base
.modifier
)
183 ret
= komeda_fb_afbc_size_check(kfb
, file
, mode_cmd
);
185 ret
= komeda_fb_none_afbc_size_check(mdev
, kfb
, file
, mode_cmd
);
189 ret
= drm_framebuffer_init(dev
, &kfb
->base
, &komeda_fb_funcs
);
191 DRM_DEBUG_KMS("failed to initialize fb\n");
196 kfb
->is_va
= mdev
->iommu
? true : false;
201 for (i
= 0; i
< kfb
->base
.format
->num_planes
; i
++)
202 drm_gem_object_put_unlocked(kfb
->base
.obj
[i
]);
208 int komeda_fb_check_src_coords(const struct komeda_fb
*kfb
,
209 u32 src_x
, u32 src_y
, u32 src_w
, u32 src_h
)
211 const struct drm_framebuffer
*fb
= &kfb
->base
;
212 const struct drm_format_info
*info
= fb
->format
;
213 u32 block_w
= drm_format_info_block_width(fb
->format
, 0);
214 u32 block_h
= drm_format_info_block_height(fb
->format
, 0);
216 if ((src_x
+ src_w
> fb
->width
) || (src_y
+ src_h
> fb
->height
)) {
217 DRM_DEBUG_ATOMIC("Invalid source coordinate.\n");
221 if ((src_x
% info
->hsub
) || (src_w
% info
->hsub
) ||
222 (src_y
% info
->vsub
) || (src_h
% info
->vsub
)) {
223 DRM_DEBUG_ATOMIC("Wrong subsampling dimension x:%d, y:%d, w:%d, h:%d for format: %x.\n",
224 src_x
, src_y
, src_w
, src_h
, info
->format
);
228 if ((src_x
% block_w
) || (src_w
% block_w
) ||
229 (src_y
% block_h
) || (src_h
% block_h
)) {
230 DRM_DEBUG_ATOMIC("x:%d, y:%d, w:%d, h:%d should be multiple of block_w/h for format: %x.\n",
231 src_x
, src_y
, src_w
, src_h
, info
->format
);
239 komeda_fb_get_pixel_addr(struct komeda_fb
*kfb
, int x
, int y
, int plane
)
241 struct drm_framebuffer
*fb
= &kfb
->base
;
242 const struct drm_gem_cma_object
*obj
;
243 u32 offset
, plane_x
, plane_y
, block_w
, block_sz
;
245 if (plane
>= fb
->format
->num_planes
) {
246 DRM_DEBUG_KMS("Out of max plane num.\n");
250 obj
= drm_fb_cma_get_gem_obj(fb
, plane
);
252 offset
= fb
->offsets
[plane
];
254 block_w
= drm_format_info_block_width(fb
->format
, plane
);
255 block_sz
= fb
->format
->char_per_block
[plane
];
256 plane_x
= x
/ (plane
? fb
->format
->hsub
: 1);
257 plane_y
= y
/ (plane
? fb
->format
->vsub
: 1);
259 offset
+= (plane_x
/ block_w
) * block_sz
260 + plane_y
* fb
->pitches
[plane
];
263 return obj
->paddr
+ offset
;
266 /* if the fb can be supported by a specific layer */
267 bool komeda_fb_is_layer_supported(struct komeda_fb
*kfb
, u32 layer_type
,
270 struct drm_framebuffer
*fb
= &kfb
->base
;
271 struct komeda_dev
*mdev
= fb
->dev
->dev_private
;
272 u32 fourcc
= fb
->format
->format
;
273 u64 modifier
= fb
->modifier
;
276 supported
= komeda_format_mod_supported(&mdev
->fmt_tbl
, layer_type
,
277 fourcc
, modifier
, rot
);
279 DRM_DEBUG_ATOMIC("Layer TYPE: %d doesn't support fb FMT: %s.\n",
280 layer_type
, komeda_get_format_name(fourcc
, modifier
));