0001
0002
0003
0004
0005
0006
0007 #include <drm/drm_device.h>
0008 #include <drm/drm_fb_cma_helper.h>
0009 #include <drm/drm_gem.h>
0010 #include <drm/drm_gem_cma_helper.h>
0011 #include <drm/drm_gem_framebuffer_helper.h>
0012
0013 #include "komeda_framebuffer.h"
0014 #include "komeda_dev.h"
0015
0016 static void komeda_fb_destroy(struct drm_framebuffer *fb)
0017 {
0018 struct komeda_fb *kfb = to_kfb(fb);
0019 u32 i;
0020
0021 for (i = 0; i < fb->format->num_planes; i++)
0022 drm_gem_object_put(fb->obj[i]);
0023
0024 drm_framebuffer_cleanup(fb);
0025 kfree(kfb);
0026 }
0027
0028 static int komeda_fb_create_handle(struct drm_framebuffer *fb,
0029 struct drm_file *file, u32 *handle)
0030 {
0031 return drm_gem_handle_create(file, fb->obj[0], handle);
0032 }
0033
0034 static const struct drm_framebuffer_funcs komeda_fb_funcs = {
0035 .destroy = komeda_fb_destroy,
0036 .create_handle = komeda_fb_create_handle,
0037 };
0038
0039 static int
0040 komeda_fb_afbc_size_check(struct komeda_fb *kfb, struct drm_file *file,
0041 const struct drm_mode_fb_cmd2 *mode_cmd)
0042 {
0043 struct drm_framebuffer *fb = &kfb->base;
0044 const struct drm_format_info *info = fb->format;
0045 struct drm_gem_object *obj;
0046 u32 alignment_w = 0, alignment_h = 0, alignment_header, n_blocks, bpp;
0047 u64 min_size;
0048
0049 obj = drm_gem_object_lookup(file, mode_cmd->handles[0]);
0050 if (!obj) {
0051 DRM_DEBUG_KMS("Failed to lookup GEM object\n");
0052 return -ENOENT;
0053 }
0054
0055 switch (fb->modifier & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK) {
0056 case AFBC_FORMAT_MOD_BLOCK_SIZE_32x8:
0057 alignment_w = 32;
0058 alignment_h = 8;
0059 break;
0060 case AFBC_FORMAT_MOD_BLOCK_SIZE_16x16:
0061 alignment_w = 16;
0062 alignment_h = 16;
0063 break;
0064 default:
0065 WARN(1, "Invalid AFBC_FORMAT_MOD_BLOCK_SIZE: %lld.\n",
0066 fb->modifier & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK);
0067 break;
0068 }
0069
0070
0071 if (fb->modifier & AFBC_FORMAT_MOD_TILED) {
0072 alignment_w *= AFBC_TH_LAYOUT_ALIGNMENT;
0073 alignment_h *= AFBC_TH_LAYOUT_ALIGNMENT;
0074 alignment_header = AFBC_TH_BODY_START_ALIGNMENT;
0075 } else {
0076 alignment_header = AFBC_BODY_START_ALIGNMENT;
0077 }
0078
0079 kfb->aligned_w = ALIGN(fb->width, alignment_w);
0080 kfb->aligned_h = ALIGN(fb->height, alignment_h);
0081
0082 if (fb->offsets[0] % alignment_header) {
0083 DRM_DEBUG_KMS("afbc offset alignment check failed.\n");
0084 goto check_failed;
0085 }
0086
0087 n_blocks = (kfb->aligned_w * kfb->aligned_h) / AFBC_SUPERBLK_PIXELS;
0088 kfb->offset_payload = ALIGN(n_blocks * AFBC_HEADER_SIZE,
0089 alignment_header);
0090
0091 bpp = komeda_get_afbc_format_bpp(info, fb->modifier);
0092 kfb->afbc_size = kfb->offset_payload + n_blocks *
0093 ALIGN(bpp * AFBC_SUPERBLK_PIXELS / 8,
0094 AFBC_SUPERBLK_ALIGNMENT);
0095 min_size = kfb->afbc_size + fb->offsets[0];
0096 if (min_size > obj->size) {
0097 DRM_DEBUG_KMS("afbc size check failed, obj_size: 0x%zx. min_size 0x%llx.\n",
0098 obj->size, min_size);
0099 goto check_failed;
0100 }
0101
0102 fb->obj[0] = obj;
0103 return 0;
0104
0105 check_failed:
0106 drm_gem_object_put(obj);
0107 return -EINVAL;
0108 }
0109
0110 static int
0111 komeda_fb_none_afbc_size_check(struct komeda_dev *mdev, struct komeda_fb *kfb,
0112 struct drm_file *file,
0113 const struct drm_mode_fb_cmd2 *mode_cmd)
0114 {
0115 struct drm_framebuffer *fb = &kfb->base;
0116 const struct drm_format_info *info = fb->format;
0117 struct drm_gem_object *obj;
0118 u32 i, block_h;
0119 u64 min_size;
0120
0121 if (komeda_fb_check_src_coords(kfb, 0, 0, fb->width, fb->height))
0122 return -EINVAL;
0123
0124 for (i = 0; i < info->num_planes; i++) {
0125 obj = drm_gem_object_lookup(file, mode_cmd->handles[i]);
0126 if (!obj) {
0127 DRM_DEBUG_KMS("Failed to lookup GEM object\n");
0128 return -ENOENT;
0129 }
0130 fb->obj[i] = obj;
0131
0132 block_h = drm_format_info_block_height(info, i);
0133 if ((fb->pitches[i] * block_h) % mdev->chip.bus_width) {
0134 DRM_DEBUG_KMS("Pitch[%d]: 0x%x doesn't align to 0x%x\n",
0135 i, fb->pitches[i], mdev->chip.bus_width);
0136 return -EINVAL;
0137 }
0138
0139 min_size = komeda_fb_get_pixel_addr(kfb, 0, fb->height, i)
0140 - to_drm_gem_cma_obj(obj)->paddr;
0141 if (obj->size < min_size) {
0142 DRM_DEBUG_KMS("The fb->obj[%d] size: 0x%zx lower than the minimum requirement: 0x%llx.\n",
0143 i, obj->size, min_size);
0144 return -EINVAL;
0145 }
0146 }
0147
0148 if (fb->format->num_planes == 3) {
0149 if (fb->pitches[1] != fb->pitches[2]) {
0150 DRM_DEBUG_KMS("The pitch[1] and [2] are not same\n");
0151 return -EINVAL;
0152 }
0153 }
0154
0155 return 0;
0156 }
0157
0158 struct drm_framebuffer *
0159 komeda_fb_create(struct drm_device *dev, struct drm_file *file,
0160 const struct drm_mode_fb_cmd2 *mode_cmd)
0161 {
0162 struct komeda_dev *mdev = dev->dev_private;
0163 struct komeda_fb *kfb;
0164 int ret = 0, i;
0165
0166 kfb = kzalloc(sizeof(*kfb), GFP_KERNEL);
0167 if (!kfb)
0168 return ERR_PTR(-ENOMEM);
0169
0170 kfb->format_caps = komeda_get_format_caps(&mdev->fmt_tbl,
0171 mode_cmd->pixel_format,
0172 mode_cmd->modifier[0]);
0173 if (!kfb->format_caps) {
0174 DRM_DEBUG_KMS("FMT %x is not supported.\n",
0175 mode_cmd->pixel_format);
0176 kfree(kfb);
0177 return ERR_PTR(-EINVAL);
0178 }
0179
0180 drm_helper_mode_fill_fb_struct(dev, &kfb->base, mode_cmd);
0181
0182 if (kfb->base.modifier)
0183 ret = komeda_fb_afbc_size_check(kfb, file, mode_cmd);
0184 else
0185 ret = komeda_fb_none_afbc_size_check(mdev, kfb, file, mode_cmd);
0186 if (ret < 0)
0187 goto err_cleanup;
0188
0189 ret = drm_framebuffer_init(dev, &kfb->base, &komeda_fb_funcs);
0190 if (ret < 0) {
0191 DRM_DEBUG_KMS("failed to initialize fb\n");
0192
0193 goto err_cleanup;
0194 }
0195
0196 kfb->is_va = mdev->iommu ? true : false;
0197
0198 return &kfb->base;
0199
0200 err_cleanup:
0201 for (i = 0; i < kfb->base.format->num_planes; i++)
0202 drm_gem_object_put(kfb->base.obj[i]);
0203
0204 kfree(kfb);
0205 return ERR_PTR(ret);
0206 }
0207
0208 int komeda_fb_check_src_coords(const struct komeda_fb *kfb,
0209 u32 src_x, u32 src_y, u32 src_w, u32 src_h)
0210 {
0211 const struct drm_framebuffer *fb = &kfb->base;
0212 const struct drm_format_info *info = fb->format;
0213 u32 block_w = drm_format_info_block_width(fb->format, 0);
0214 u32 block_h = drm_format_info_block_height(fb->format, 0);
0215
0216 if ((src_x + src_w > fb->width) || (src_y + src_h > fb->height)) {
0217 DRM_DEBUG_ATOMIC("Invalid source coordinate.\n");
0218 return -EINVAL;
0219 }
0220
0221 if ((src_x % info->hsub) || (src_w % info->hsub) ||
0222 (src_y % info->vsub) || (src_h % info->vsub)) {
0223 DRM_DEBUG_ATOMIC("Wrong subsampling dimension x:%d, y:%d, w:%d, h:%d for format: %x.\n",
0224 src_x, src_y, src_w, src_h, info->format);
0225 return -EINVAL;
0226 }
0227
0228 if ((src_x % block_w) || (src_w % block_w) ||
0229 (src_y % block_h) || (src_h % block_h)) {
0230 DRM_DEBUG_ATOMIC("x:%d, y:%d, w:%d, h:%d should be multiple of block_w/h for format: %x.\n",
0231 src_x, src_y, src_w, src_h, info->format);
0232 return -EINVAL;
0233 }
0234
0235 return 0;
0236 }
0237
0238 dma_addr_t
0239 komeda_fb_get_pixel_addr(struct komeda_fb *kfb, int x, int y, int plane)
0240 {
0241 struct drm_framebuffer *fb = &kfb->base;
0242 const struct drm_gem_cma_object *obj;
0243 u32 offset, plane_x, plane_y, block_w, block_sz;
0244
0245 if (plane >= fb->format->num_planes) {
0246 DRM_DEBUG_KMS("Out of max plane num.\n");
0247 return -EINVAL;
0248 }
0249
0250 obj = drm_fb_cma_get_gem_obj(fb, plane);
0251
0252 offset = fb->offsets[plane];
0253 if (!fb->modifier) {
0254 block_w = drm_format_info_block_width(fb->format, plane);
0255 block_sz = fb->format->char_per_block[plane];
0256 plane_x = x / (plane ? fb->format->hsub : 1);
0257 plane_y = y / (plane ? fb->format->vsub : 1);
0258
0259 offset += (plane_x / block_w) * block_sz
0260 + plane_y * fb->pitches[plane];
0261 }
0262
0263 return obj->paddr + offset;
0264 }
0265
0266
0267 bool komeda_fb_is_layer_supported(struct komeda_fb *kfb, u32 layer_type,
0268 u32 rot)
0269 {
0270 struct drm_framebuffer *fb = &kfb->base;
0271 struct komeda_dev *mdev = fb->dev->dev_private;
0272 u32 fourcc = fb->format->format;
0273 u64 modifier = fb->modifier;
0274 bool supported;
0275
0276 supported = komeda_format_mod_supported(&mdev->fmt_tbl, layer_type,
0277 fourcc, modifier, rot);
0278 if (!supported)
0279 DRM_DEBUG_ATOMIC("Layer TYPE: %d doesn't support fb FMT: %p4cc with modifier: 0x%llx.\n",
0280 layer_type, &fourcc, modifier);
0281
0282 return supported;
0283 }