Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * (C) COPYRIGHT 2018 ARM Limited. All rights reserved.
0004  * Author: James.Qian.Wang <james.qian.wang@arm.com>
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     /* tiled header afbc */
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 /* if the fb can be supported by a specific layer */
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 }