1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * (C) COPYRIGHT 2018 ARM Limited. All rights reserved. 4 * Author: James.Qian.Wang <james.qian.wang@arm.com> 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