linux/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.c
<<
>>
Prefs
   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
  16static 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(fb->obj[i]);
  23
  24        drm_framebuffer_cleanup(fb);
  25        kfree(kfb);
  26}
  27
  28static 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
  34static const struct drm_framebuffer_funcs komeda_fb_funcs = {
  35        .destroy        = komeda_fb_destroy,
  36        .create_handle  = komeda_fb_create_handle,
  37};
  38
  39static int
  40komeda_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
 105check_failed:
 106        drm_gem_object_put(obj);
 107        return -EINVAL;
 108}
 109
 110static int
 111komeda_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
 158struct drm_framebuffer *
 159komeda_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
 200err_cleanup:
 201        for (i = 0; i < kfb->base.format->num_planes; i++)
 202                drm_gem_object_put(kfb->base.obj[i]);
 203
 204        kfree(kfb);
 205        return ERR_PTR(ret);
 206}
 207
 208int 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
 238dma_addr_t
 239komeda_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 */
 267bool 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: %p4cc with modifier: 0x%llx.\n",
 280                                 layer_type, &fourcc, modifier);
 281
 282        return supported;
 283}
 284