root/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. komeda_fb_destroy
  2. komeda_fb_create_handle
  3. komeda_fb_afbc_size_check
  4. komeda_fb_none_afbc_size_check
  5. komeda_fb_create
  6. komeda_fb_check_src_coords
  7. komeda_fb_get_pixel_addr
  8. komeda_fb_is_layer_supported

   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 }

/* [<][>][^][v][top][bottom][index][help] */