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; 47 u32 n_blocks = 0, min_size = 0; 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%lx. min_size 0x%x.\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 struct drm_gem_object *obj; 116 u32 min_size = 0; 117 u32 i; 118 119 for (i = 0; i < fb->format->num_planes; i++) { 120 obj = drm_gem_object_lookup(file, mode_cmd->handles[i]); 121 if (!obj) { 122 DRM_DEBUG_KMS("Failed to lookup GEM object\n"); 123 fb->obj[i] = NULL; 124 125 return -ENOENT; 126 } 127 128 kfb->aligned_w = fb->width / (i ? fb->format->hsub : 1); 129 kfb->aligned_h = fb->height / (i ? fb->format->vsub : 1); 130 131 if (fb->pitches[i] % mdev->chip.bus_width) { 132 DRM_DEBUG_KMS("Pitch[%d]: 0x%x doesn't align to 0x%x\n", 133 i, fb->pitches[i], mdev->chip.bus_width); 134 drm_gem_object_put_unlocked(obj); 135 fb->obj[i] = NULL; 136 137 return -EINVAL; 138 } 139 140 min_size = ((kfb->aligned_h / kfb->format_caps->tile_size - 1) 141 * fb->pitches[i]) 142 + (kfb->aligned_w * fb->format->cpp[i] 143 * kfb->format_caps->tile_size) 144 + fb->offsets[i]; 145 146 if (obj->size < min_size) { 147 DRM_DEBUG_KMS("Fail to check none afbc fb size.\n"); 148 drm_gem_object_put_unlocked(obj); 149 fb->obj[i] = NULL; 150 151 return -EINVAL; 152 } 153 154 fb->obj[i] = obj; 155 } 156 157 if (fb->format->num_planes == 3) { 158 if (fb->pitches[1] != fb->pitches[2]) { 159 DRM_DEBUG_KMS("The pitch[1] and [2] are not same\n"); 160 return -EINVAL; 161 } 162 } 163 164 return 0; 165 } 166 167 struct drm_framebuffer * 168 komeda_fb_create(struct drm_device *dev, struct drm_file *file, 169 const struct drm_mode_fb_cmd2 *mode_cmd) 170 { 171 struct komeda_dev *mdev = dev->dev_private; 172 struct komeda_fb *kfb; 173 int ret = 0, i; 174 175 kfb = kzalloc(sizeof(*kfb), GFP_KERNEL); 176 if (!kfb) 177 return ERR_PTR(-ENOMEM); 178 179 kfb->format_caps = komeda_get_format_caps(&mdev->fmt_tbl, 180 mode_cmd->pixel_format, 181 mode_cmd->modifier[0]); 182 if (!kfb->format_caps) { 183 DRM_DEBUG_KMS("FMT %x is not supported.\n", 184 mode_cmd->pixel_format); 185 kfree(kfb); 186 return ERR_PTR(-EINVAL); 187 } 188 189 drm_helper_mode_fill_fb_struct(dev, &kfb->base, mode_cmd); 190 191 if (kfb->base.modifier) 192 ret = komeda_fb_afbc_size_check(kfb, file, mode_cmd); 193 else 194 ret = komeda_fb_none_afbc_size_check(mdev, kfb, file, mode_cmd); 195 if (ret < 0) 196 goto err_cleanup; 197 198 ret = drm_framebuffer_init(dev, &kfb->base, &komeda_fb_funcs); 199 if (ret < 0) { 200 DRM_DEBUG_KMS("failed to initialize fb\n"); 201 202 goto err_cleanup; 203 } 204 205 return &kfb->base; 206 207 err_cleanup: 208 for (i = 0; i < kfb->base.format->num_planes; i++) 209 drm_gem_object_put_unlocked(kfb->base.obj[i]); 210 211 kfree(kfb); 212 return ERR_PTR(ret); 213 } 214 215 dma_addr_t 216 komeda_fb_get_pixel_addr(struct komeda_fb *kfb, int x, int y, int plane) 217 { 218 struct drm_framebuffer *fb = &kfb->base; 219 const struct drm_gem_cma_object *obj; 220 u32 plane_x, plane_y, cpp, pitch, offset; 221 222 if (plane >= fb->format->num_planes) { 223 DRM_DEBUG_KMS("Out of max plane num.\n"); 224 return -EINVAL; 225 } 226 227 obj = drm_fb_cma_get_gem_obj(fb, plane); 228 229 offset = fb->offsets[plane]; 230 if (!fb->modifier) { 231 plane_x = x / (plane ? fb->format->hsub : 1); 232 plane_y = y / (plane ? fb->format->vsub : 1); 233 cpp = fb->format->cpp[plane]; 234 pitch = fb->pitches[plane]; 235 offset += plane_x * cpp * kfb->format_caps->tile_size + 236 (plane_y * pitch) / kfb->format_caps->tile_size; 237 } 238 239 return obj->paddr + offset; 240 } 241 242 /* if the fb can be supported by a specific layer */ 243 bool komeda_fb_is_layer_supported(struct komeda_fb *kfb, u32 layer_type) 244 { 245 struct drm_framebuffer *fb = &kfb->base; 246 struct komeda_dev *mdev = fb->dev->dev_private; 247 const struct komeda_format_caps *caps; 248 u32 fourcc = fb->format->format; 249 u64 modifier = fb->modifier; 250 251 caps = komeda_get_format_caps(&mdev->fmt_tbl, fourcc, modifier); 252 if (!caps) 253 return false; 254 255 if (!(caps->supported_layer_types & layer_type)) 256 return false; 257 258 return true; 259 } 260