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 13 #include "komeda_framebuffer.h" 14 #include "komeda_dev.h" 15 16 static void komeda_fb_destroy(struct drm_framebuffer *fb) 17 { 18 struct komeda_fb *kfb = to_kfb(fb); 19 u32 i; 20 21 for (i = 0; i < fb->format->num_planes; i++) 22 drm_gem_object_put(fb->obj[i]); 23 24 drm_framebuffer_cleanup(fb); 25 kfree(kfb); 26 } 27 28 static int komeda_fb_create_handle(struct drm_framebuffer *fb, 29 struct drm_file *file, u32 *handle) 30 { 31 return drm_gem_handle_create(file, fb->obj[0], handle); 32 } 33 34 static const struct drm_framebuffer_funcs komeda_fb_funcs = { 35 .destroy = komeda_fb_destroy, 36 .create_handle = komeda_fb_create_handle, 37 }; 38 39 static int 40 komeda_fb_afbc_size_check(struct komeda_fb *kfb, struct drm_file *file, 41 const struct drm_mode_fb_cmd2 *mode_cmd) 42 { 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; 47 u64 min_size; 48 49 obj = drm_gem_object_lookup(file, mode_cmd->handles[0]); 50 if (!obj) { 51 DRM_DEBUG_KMS("Failed to lookup GEM object\n"); 52 return -ENOENT; 53 } 54 55 switch (fb->modifier & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK) { 56 case AFBC_FORMAT_MOD_BLOCK_SIZE_32x8: 57 alignment_w = 32; 58 alignment_h = 8; 59 break; 60 case AFBC_FORMAT_MOD_BLOCK_SIZE_16x16: 61 alignment_w = 16; 62 alignment_h = 16; 63 break; 64 default: 65 WARN(1, "Invalid AFBC_FORMAT_MOD_BLOCK_SIZE: %lld.\n", 66 fb->modifier & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK); 67 break; 68 } 69 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; 75 } else { 76 alignment_header = AFBC_BODY_START_ALIGNMENT; 77 } 78 79 kfb->aligned_w = ALIGN(fb->width, alignment_w); 80 kfb->aligned_h = ALIGN(fb->height, alignment_h); 81 82 if (fb->offsets[0] % alignment_header) { 83 DRM_DEBUG_KMS("afbc offset alignment check failed.\n"); 84 goto check_failed; 85 } 86 87 n_blocks = (kfb->aligned_w * kfb->aligned_h) / AFBC_SUPERBLK_PIXELS; 88 kfb->offset_payload = ALIGN(n_blocks * AFBC_HEADER_SIZE, 89 alignment_header); 90 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", 98 obj->size, min_size); 99 goto check_failed; 100 } 101 102 fb->obj[0] = obj; 103 return 0; 104 105 check_failed: 106 drm_gem_object_put(obj); 107 return -EINVAL; 108 } 109 110 static int 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) 114 { 115 struct drm_framebuffer *fb = &kfb->base; 116 const struct drm_format_info *info = fb->format; 117 struct drm_gem_object *obj; 118 u32 i, block_h; 119 u64 min_size; 120 121 if (komeda_fb_check_src_coords(kfb, 0, 0, fb->width, fb->height)) 122 return -EINVAL; 123 124 for (i = 0; i < info->num_planes; i++) { 125 obj = drm_gem_object_lookup(file, mode_cmd->handles[i]); 126 if (!obj) { 127 DRM_DEBUG_KMS("Failed to lookup GEM object\n"); 128 return -ENOENT; 129 } 130 fb->obj[i] = obj; 131 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); 136 return -EINVAL; 137 } 138 139 min_size = komeda_fb_get_pixel_addr(kfb, 0, fb->height, i) 140 - to_drm_gem_dma_obj(obj)->dma_addr; 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); 144 return -EINVAL; 145 } 146 } 147 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"); 151 return -EINVAL; 152 } 153 } 154 155 return 0; 156 } 157 158 struct drm_framebuffer * 159 komeda_fb_create(struct drm_device *dev, struct drm_file *file, 160 const struct drm_format_info *info, 161 const struct drm_mode_fb_cmd2 *mode_cmd) 162 { 163 struct komeda_dev *mdev = dev->dev_private; 164 struct komeda_fb *kfb; 165 int ret = 0, i; 166 167 kfb = kzalloc(sizeof(*kfb), GFP_KERNEL); 168 if (!kfb) 169 return ERR_PTR(-ENOMEM); 170 171 kfb->format_caps = komeda_get_format_caps(&mdev->fmt_tbl, 172 mode_cmd->pixel_format, 173 mode_cmd->modifier[0]); 174 if (!kfb->format_caps) { 175 DRM_DEBUG_KMS("FMT %x is not supported.\n", 176 mode_cmd->pixel_format); 177 kfree(kfb); 178 return ERR_PTR(-EINVAL); 179 } 180 181 drm_helper_mode_fill_fb_struct(dev, &kfb->base, info, mode_cmd); 182 183 if (kfb->base.modifier) 184 ret = komeda_fb_afbc_size_check(kfb, file, mode_cmd); 185 else 186 ret = komeda_fb_none_afbc_size_check(mdev, kfb, file, mode_cmd); 187 if (ret < 0) 188 goto err_cleanup; 189 190 ret = drm_framebuffer_init(dev, &kfb->base, &komeda_fb_funcs); 191 if (ret < 0) { 192 DRM_DEBUG_KMS("failed to initialize fb\n"); 193 194 goto err_cleanup; 195 } 196 197 kfb->is_va = mdev->iommu ? true : false; 198 199 return &kfb->base; 200 201 err_cleanup: 202 for (i = 0; i < kfb->base.format->num_planes; i++) 203 drm_gem_object_put(kfb->base.obj[i]); 204 205 kfree(kfb); 206 return ERR_PTR(ret); 207 } 208 209 int komeda_fb_check_src_coords(const struct komeda_fb *kfb, 210 u32 src_x, u32 src_y, u32 src_w, u32 src_h) 211 { 212 const struct drm_framebuffer *fb = &kfb->base; 213 const struct drm_format_info *info = fb->format; 214 u32 block_w = drm_format_info_block_width(fb->format, 0); 215 u32 block_h = drm_format_info_block_height(fb->format, 0); 216 217 if ((src_x + src_w > fb->width) || (src_y + src_h > fb->height)) { 218 DRM_DEBUG_ATOMIC("Invalid source coordinate.\n"); 219 return -EINVAL; 220 } 221 222 if ((src_x % info->hsub) || (src_w % info->hsub) || 223 (src_y % info->vsub) || (src_h % info->vsub)) { 224 DRM_DEBUG_ATOMIC("Wrong subsampling dimension x:%d, y:%d, w:%d, h:%d for format: %x.\n", 225 src_x, src_y, src_w, src_h, info->format); 226 return -EINVAL; 227 } 228 229 if ((src_x % block_w) || (src_w % block_w) || 230 (src_y % block_h) || (src_h % block_h)) { 231 DRM_DEBUG_ATOMIC("x:%d, y:%d, w:%d, h:%d should be multiple of block_w/h for format: %x.\n", 232 src_x, src_y, src_w, src_h, info->format); 233 return -EINVAL; 234 } 235 236 return 0; 237 } 238 239 dma_addr_t 240 komeda_fb_get_pixel_addr(struct komeda_fb *kfb, int x, int y, int plane) 241 { 242 struct drm_framebuffer *fb = &kfb->base; 243 const struct drm_gem_dma_object *obj; 244 u32 offset, plane_x, plane_y, block_w, block_sz; 245 246 if (plane >= fb->format->num_planes) { 247 DRM_DEBUG_KMS("Out of max plane num.\n"); 248 return -EINVAL; 249 } 250 251 obj = drm_fb_dma_get_gem_obj(fb, plane); 252 253 offset = fb->offsets[plane]; 254 if (!fb->modifier) { 255 block_w = drm_format_info_block_width(fb->format, plane); 256 block_sz = fb->format->char_per_block[plane]; 257 plane_x = x / (plane ? fb->format->hsub : 1); 258 plane_y = y / (plane ? fb->format->vsub : 1); 259 260 offset += (plane_x / block_w) * block_sz 261 + plane_y * fb->pitches[plane]; 262 } 263 264 return obj->dma_addr + offset; 265 } 266 267 /* if the fb can be supported by a specific layer */ 268 bool komeda_fb_is_layer_supported(struct komeda_fb *kfb, u32 layer_type, 269 u32 rot) 270 { 271 struct drm_framebuffer *fb = &kfb->base; 272 struct komeda_dev *mdev = fb->dev->dev_private; 273 u32 fourcc = fb->format->format; 274 u64 modifier = fb->modifier; 275 bool supported; 276 277 supported = komeda_format_mod_supported(&mdev->fmt_tbl, layer_type, 278 fourcc, modifier, rot); 279 if (!supported) 280 DRM_DEBUG_ATOMIC("Layer TYPE: %d doesn't support fb FMT: %p4cc with modifier: 0x%llx.\n", 281 layer_type, &fourcc, modifier); 282 283 return supported; 284 } 285