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_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> 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_unlocked(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_unlocked(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_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); 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_mode_fb_cmd2 *mode_cmd) 161 { 162 struct komeda_dev *mdev = dev->dev_private; 163 struct komeda_fb *kfb; 164 int ret = 0, i; 165 166 kfb = kzalloc(sizeof(*kfb), GFP_KERNEL); 167 if (!kfb) 168 return ERR_PTR(-ENOMEM); 169 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); 176 kfree(kfb); 177 return ERR_PTR(-EINVAL); 178 } 179 180 drm_helper_mode_fill_fb_struct(dev, &kfb->base, mode_cmd); 181 182 if (kfb->base.modifier) 183 ret = komeda_fb_afbc_size_check(kfb, file, mode_cmd); 184 else 185 ret = komeda_fb_none_afbc_size_check(mdev, kfb, file, mode_cmd); 186 if (ret < 0) 187 goto err_cleanup; 188 189 ret = drm_framebuffer_init(dev, &kfb->base, &komeda_fb_funcs); 190 if (ret < 0) { 191 DRM_DEBUG_KMS("failed to initialize fb\n"); 192 193 goto err_cleanup; 194 } 195 196 kfb->is_va = mdev->iommu ? true : false; 197 198 return &kfb->base; 199 200 err_cleanup: 201 for (i = 0; i < kfb->base.format->num_planes; i++) 202 drm_gem_object_put_unlocked(kfb->base.obj[i]); 203 204 kfree(kfb); 205 return ERR_PTR(ret); 206 } 207 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) 210 { 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); 215 216 if ((src_x + src_w > fb->width) || (src_y + src_h > fb->height)) { 217 DRM_DEBUG_ATOMIC("Invalid source coordinate.\n"); 218 return -EINVAL; 219 } 220 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); 225 return -EINVAL; 226 } 227 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); 232 return -EINVAL; 233 } 234 235 return 0; 236 } 237 238 dma_addr_t 239 komeda_fb_get_pixel_addr(struct komeda_fb *kfb, int x, int y, int plane) 240 { 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; 244 245 if (plane >= fb->format->num_planes) { 246 DRM_DEBUG_KMS("Out of max plane num.\n"); 247 return -EINVAL; 248 } 249 250 obj = drm_fb_cma_get_gem_obj(fb, plane); 251 252 offset = fb->offsets[plane]; 253 if (!fb->modifier) { 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); 258 259 offset += (plane_x / block_w) * block_sz 260 + plane_y * fb->pitches[plane]; 261 } 262 263 return obj->paddr + offset; 264 } 265 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, 268 u32 rot) 269 { 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; 274 bool supported; 275 276 supported = komeda_format_mod_supported(&mdev->fmt_tbl, layer_type, 277 fourcc, modifier, rot); 278 if (!supported) 279 DRM_DEBUG_ATOMIC("Layer TYPE: %d doesn't support fb FMT: %s.\n", 280 layer_type, komeda_get_format_name(fourcc, modifier)); 281 282 return supported; 283 } 284