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; 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 kfb->afbc_size = kfb->offset_payload + n_blocks * 92 ALIGN(info->cpp[0] * AFBC_SUPERBLK_PIXELS, 93 AFBC_SUPERBLK_ALIGNMENT); 94 min_size = kfb->afbc_size + fb->offsets[0]; 95 if (min_size > obj->size) { 96 DRM_DEBUG_KMS("afbc size check failed, obj_size: 0x%zx. min_size 0x%llx.\n", 97 obj->size, min_size); 98 goto check_failed; 99 } 100 101 fb->obj[0] = obj; 102 return 0; 103 104 check_failed: 105 drm_gem_object_put_unlocked(obj); 106 return -EINVAL; 107 } 108 109 static int 110 komeda_fb_none_afbc_size_check(struct komeda_dev *mdev, struct komeda_fb *kfb, 111 struct drm_file *file, 112 const struct drm_mode_fb_cmd2 *mode_cmd) 113 { 114 struct drm_framebuffer *fb = &kfb->base; 115 const struct drm_format_info *info = fb->format; 116 struct drm_gem_object *obj; 117 u32 i, block_h; 118 u64 min_size; 119 120 if (komeda_fb_check_src_coords(kfb, 0, 0, fb->width, fb->height)) 121 return -EINVAL; 122 123 for (i = 0; i < info->num_planes; i++) { 124 obj = drm_gem_object_lookup(file, mode_cmd->handles[i]); 125 if (!obj) { 126 DRM_DEBUG_KMS("Failed to lookup GEM object\n"); 127 return -ENOENT; 128 } 129 fb->obj[i] = obj; 130 131 block_h = drm_format_info_block_height(info, i); 132 if ((fb->pitches[i] * block_h) % mdev->chip.bus_width) { 133 DRM_DEBUG_KMS("Pitch[%d]: 0x%x doesn't align to 0x%x\n", 134 i, fb->pitches[i], mdev->chip.bus_width); 135 return -EINVAL; 136 } 137 138 min_size = komeda_fb_get_pixel_addr(kfb, 0, fb->height, i) 139 - to_drm_gem_cma_obj(obj)->paddr; 140 if (obj->size < min_size) { 141 DRM_DEBUG_KMS("The fb->obj[%d] size: 0x%zx lower than the minimum requirement: 0x%llx.\n", 142 i, obj->size, min_size); 143 return -EINVAL; 144 } 145 } 146 147 if (fb->format->num_planes == 3) { 148 if (fb->pitches[1] != fb->pitches[2]) { 149 DRM_DEBUG_KMS("The pitch[1] and [2] are not same\n"); 150 return -EINVAL; 151 } 152 } 153 154 return 0; 155 } 156 157 struct drm_framebuffer * 158 komeda_fb_create(struct drm_device *dev, struct drm_file *file, 159 const struct drm_mode_fb_cmd2 *mode_cmd) 160 { 161 struct komeda_dev *mdev = dev->dev_private; 162 struct komeda_fb *kfb; 163 int ret = 0, i; 164 165 kfb = kzalloc(sizeof(*kfb), GFP_KERNEL); 166 if (!kfb) 167 return ERR_PTR(-ENOMEM); 168 169 kfb->format_caps = komeda_get_format_caps(&mdev->fmt_tbl, 170 mode_cmd->pixel_format, 171 mode_cmd->modifier[0]); 172 if (!kfb->format_caps) { 173 DRM_DEBUG_KMS("FMT %x is not supported.\n", 174 mode_cmd->pixel_format); 175 kfree(kfb); 176 return ERR_PTR(-EINVAL); 177 } 178 179 drm_helper_mode_fill_fb_struct(dev, &kfb->base, mode_cmd); 180 181 if (kfb->base.modifier) 182 ret = komeda_fb_afbc_size_check(kfb, file, mode_cmd); 183 else 184 ret = komeda_fb_none_afbc_size_check(mdev, kfb, file, mode_cmd); 185 if (ret < 0) 186 goto err_cleanup; 187 188 ret = drm_framebuffer_init(dev, &kfb->base, &komeda_fb_funcs); 189 if (ret < 0) { 190 DRM_DEBUG_KMS("failed to initialize fb\n"); 191 192 goto err_cleanup; 193 } 194 195 kfb->is_va = mdev->iommu ? true : false; 196 197 return &kfb->base; 198 199 err_cleanup: 200 for (i = 0; i < kfb->base.format->num_planes; i++) 201 drm_gem_object_put_unlocked(kfb->base.obj[i]); 202 203 kfree(kfb); 204 return ERR_PTR(ret); 205 } 206 207 int komeda_fb_check_src_coords(const struct komeda_fb *kfb, 208 u32 src_x, u32 src_y, u32 src_w, u32 src_h) 209 { 210 const struct drm_framebuffer *fb = &kfb->base; 211 const struct drm_format_info *info = fb->format; 212 u32 block_w = drm_format_info_block_width(fb->format, 0); 213 u32 block_h = drm_format_info_block_height(fb->format, 0); 214 215 if ((src_x + src_w > fb->width) || (src_y + src_h > fb->height)) { 216 DRM_DEBUG_ATOMIC("Invalid source coordinate.\n"); 217 return -EINVAL; 218 } 219 220 if ((src_x % info->hsub) || (src_w % info->hsub) || 221 (src_y % info->vsub) || (src_h % info->vsub)) { 222 DRM_DEBUG_ATOMIC("Wrong subsampling dimension x:%d, y:%d, w:%d, h:%d for format: %x.\n", 223 src_x, src_y, src_w, src_h, info->format); 224 return -EINVAL; 225 } 226 227 if ((src_x % block_w) || (src_w % block_w) || 228 (src_y % block_h) || (src_h % block_h)) { 229 DRM_DEBUG_ATOMIC("x:%d, y:%d, w:%d, h:%d should be multiple of block_w/h for format: %x.\n", 230 src_x, src_y, src_w, src_h, info->format); 231 return -EINVAL; 232 } 233 234 return 0; 235 } 236 237 dma_addr_t 238 komeda_fb_get_pixel_addr(struct komeda_fb *kfb, int x, int y, int plane) 239 { 240 struct drm_framebuffer *fb = &kfb->base; 241 const struct drm_gem_cma_object *obj; 242 u32 offset, plane_x, plane_y, block_w, block_sz; 243 244 if (plane >= fb->format->num_planes) { 245 DRM_DEBUG_KMS("Out of max plane num.\n"); 246 return -EINVAL; 247 } 248 249 obj = drm_fb_cma_get_gem_obj(fb, plane); 250 251 offset = fb->offsets[plane]; 252 if (!fb->modifier) { 253 block_w = drm_format_info_block_width(fb->format, plane); 254 block_sz = fb->format->char_per_block[plane]; 255 plane_x = x / (plane ? fb->format->hsub : 1); 256 plane_y = y / (plane ? fb->format->vsub : 1); 257 258 offset += (plane_x / block_w) * block_sz 259 + plane_y * fb->pitches[plane]; 260 } 261 262 return obj->paddr + offset; 263 } 264 265 /* if the fb can be supported by a specific layer */ 266 bool komeda_fb_is_layer_supported(struct komeda_fb *kfb, u32 layer_type, 267 u32 rot) 268 { 269 struct drm_framebuffer *fb = &kfb->base; 270 struct komeda_dev *mdev = fb->dev->dev_private; 271 u32 fourcc = fb->format->format; 272 u64 modifier = fb->modifier; 273 bool supported; 274 275 supported = komeda_format_mod_supported(&mdev->fmt_tbl, layer_type, 276 fourcc, modifier, rot); 277 if (!supported) 278 DRM_DEBUG_ATOMIC("Layer TYPE: %d doesn't support fb FMT: %s.\n", 279 layer_type, komeda_get_format_name(fourcc, modifier)); 280 281 return supported; 282 } 283