Path: blob/master/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.c
51984 views
// SPDX-License-Identifier: GPL-2.01/*2* (C) COPYRIGHT 2018 ARM Limited. All rights reserved.3* Author: James.Qian.Wang <[email protected]>4*5*/6#include <drm/drm_device.h>7#include <drm/drm_fb_dma_helper.h>8#include <drm/drm_gem.h>9#include <drm/drm_gem_dma_helper.h>10#include <drm/drm_gem_framebuffer_helper.h>11#include <drm/drm_print.h>1213#include "komeda_framebuffer.h"14#include "komeda_dev.h"1516static void komeda_fb_destroy(struct drm_framebuffer *fb)17{18struct komeda_fb *kfb = to_kfb(fb);19u32 i;2021for (i = 0; i < fb->format->num_planes; i++)22drm_gem_object_put(fb->obj[i]);2324drm_framebuffer_cleanup(fb);25kfree(kfb);26}2728static int komeda_fb_create_handle(struct drm_framebuffer *fb,29struct drm_file *file, u32 *handle)30{31return drm_gem_handle_create(file, fb->obj[0], handle);32}3334static const struct drm_framebuffer_funcs komeda_fb_funcs = {35.destroy = komeda_fb_destroy,36.create_handle = komeda_fb_create_handle,37};3839static int40komeda_fb_afbc_size_check(struct komeda_fb *kfb, struct drm_file *file,41const struct drm_mode_fb_cmd2 *mode_cmd)42{43struct drm_framebuffer *fb = &kfb->base;44const struct drm_format_info *info = fb->format;45struct drm_gem_object *obj;46u32 alignment_w = 0, alignment_h = 0, alignment_header, n_blocks, bpp;47u64 min_size;4849obj = drm_gem_object_lookup(file, mode_cmd->handles[0]);50if (!obj) {51DRM_DEBUG_KMS("Failed to lookup GEM object\n");52return -ENOENT;53}5455switch (fb->modifier & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK) {56case AFBC_FORMAT_MOD_BLOCK_SIZE_32x8:57alignment_w = 32;58alignment_h = 8;59break;60case AFBC_FORMAT_MOD_BLOCK_SIZE_16x16:61alignment_w = 16;62alignment_h = 16;63break;64default:65WARN(1, "Invalid AFBC_FORMAT_MOD_BLOCK_SIZE: %lld.\n",66fb->modifier & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK);67break;68}6970/* tiled header afbc */71if (fb->modifier & AFBC_FORMAT_MOD_TILED) {72alignment_w *= AFBC_TH_LAYOUT_ALIGNMENT;73alignment_h *= AFBC_TH_LAYOUT_ALIGNMENT;74alignment_header = AFBC_TH_BODY_START_ALIGNMENT;75} else {76alignment_header = AFBC_BODY_START_ALIGNMENT;77}7879kfb->aligned_w = ALIGN(fb->width, alignment_w);80kfb->aligned_h = ALIGN(fb->height, alignment_h);8182if (fb->offsets[0] % alignment_header) {83DRM_DEBUG_KMS("afbc offset alignment check failed.\n");84goto check_failed;85}8687n_blocks = (kfb->aligned_w * kfb->aligned_h) / AFBC_SUPERBLK_PIXELS;88kfb->offset_payload = ALIGN(n_blocks * AFBC_HEADER_SIZE,89alignment_header);9091bpp = komeda_get_afbc_format_bpp(info, fb->modifier);92kfb->afbc_size = kfb->offset_payload + n_blocks *93ALIGN(bpp * AFBC_SUPERBLK_PIXELS / 8,94AFBC_SUPERBLK_ALIGNMENT);95min_size = kfb->afbc_size + fb->offsets[0];96if (min_size > obj->size) {97DRM_DEBUG_KMS("afbc size check failed, obj_size: 0x%zx. min_size 0x%llx.\n",98obj->size, min_size);99goto check_failed;100}101102fb->obj[0] = obj;103return 0;104105check_failed:106drm_gem_object_put(obj);107return -EINVAL;108}109110static int111komeda_fb_none_afbc_size_check(struct komeda_dev *mdev, struct komeda_fb *kfb,112struct drm_file *file,113const struct drm_mode_fb_cmd2 *mode_cmd)114{115struct drm_framebuffer *fb = &kfb->base;116const struct drm_format_info *info = fb->format;117struct drm_gem_object *obj;118u32 i, block_h;119u64 min_size;120121if (komeda_fb_check_src_coords(kfb, 0, 0, fb->width, fb->height))122return -EINVAL;123124for (i = 0; i < info->num_planes; i++) {125obj = drm_gem_object_lookup(file, mode_cmd->handles[i]);126if (!obj) {127DRM_DEBUG_KMS("Failed to lookup GEM object\n");128return -ENOENT;129}130fb->obj[i] = obj;131132block_h = drm_format_info_block_height(info, i);133if ((fb->pitches[i] * block_h) % mdev->chip.bus_width) {134DRM_DEBUG_KMS("Pitch[%d]: 0x%x doesn't align to 0x%x\n",135i, fb->pitches[i], mdev->chip.bus_width);136return -EINVAL;137}138139min_size = komeda_fb_get_pixel_addr(kfb, 0, fb->height, i)140- to_drm_gem_dma_obj(obj)->dma_addr;141if (obj->size < min_size) {142DRM_DEBUG_KMS("The fb->obj[%d] size: 0x%zx lower than the minimum requirement: 0x%llx.\n",143i, obj->size, min_size);144return -EINVAL;145}146}147148if (fb->format->num_planes == 3) {149if (fb->pitches[1] != fb->pitches[2]) {150DRM_DEBUG_KMS("The pitch[1] and [2] are not same\n");151return -EINVAL;152}153}154155return 0;156}157158struct drm_framebuffer *159komeda_fb_create(struct drm_device *dev, struct drm_file *file,160const struct drm_format_info *info,161const struct drm_mode_fb_cmd2 *mode_cmd)162{163struct komeda_dev *mdev = dev->dev_private;164struct komeda_fb *kfb;165int ret = 0, i;166167kfb = kzalloc(sizeof(*kfb), GFP_KERNEL);168if (!kfb)169return ERR_PTR(-ENOMEM);170171kfb->format_caps = komeda_get_format_caps(&mdev->fmt_tbl,172mode_cmd->pixel_format,173mode_cmd->modifier[0]);174if (!kfb->format_caps) {175DRM_DEBUG_KMS("FMT %x is not supported.\n",176mode_cmd->pixel_format);177kfree(kfb);178return ERR_PTR(-EINVAL);179}180181drm_helper_mode_fill_fb_struct(dev, &kfb->base, info, mode_cmd);182183if (kfb->base.modifier)184ret = komeda_fb_afbc_size_check(kfb, file, mode_cmd);185else186ret = komeda_fb_none_afbc_size_check(mdev, kfb, file, mode_cmd);187if (ret < 0)188goto err_cleanup;189190ret = drm_framebuffer_init(dev, &kfb->base, &komeda_fb_funcs);191if (ret < 0) {192DRM_DEBUG_KMS("failed to initialize fb\n");193194goto err_cleanup;195}196197kfb->is_va = mdev->iommu ? true : false;198199return &kfb->base;200201err_cleanup:202for (i = 0; i < kfb->base.format->num_planes; i++)203drm_gem_object_put(kfb->base.obj[i]);204205kfree(kfb);206return ERR_PTR(ret);207}208209int komeda_fb_check_src_coords(const struct komeda_fb *kfb,210u32 src_x, u32 src_y, u32 src_w, u32 src_h)211{212const struct drm_framebuffer *fb = &kfb->base;213const struct drm_format_info *info = fb->format;214u32 block_w = drm_format_info_block_width(fb->format, 0);215u32 block_h = drm_format_info_block_height(fb->format, 0);216217if ((src_x + src_w > fb->width) || (src_y + src_h > fb->height)) {218DRM_DEBUG_ATOMIC("Invalid source coordinate.\n");219return -EINVAL;220}221222if ((src_x % info->hsub) || (src_w % info->hsub) ||223(src_y % info->vsub) || (src_h % info->vsub)) {224DRM_DEBUG_ATOMIC("Wrong subsampling dimension x:%d, y:%d, w:%d, h:%d for format: %x.\n",225src_x, src_y, src_w, src_h, info->format);226return -EINVAL;227}228229if ((src_x % block_w) || (src_w % block_w) ||230(src_y % block_h) || (src_h % block_h)) {231DRM_DEBUG_ATOMIC("x:%d, y:%d, w:%d, h:%d should be multiple of block_w/h for format: %x.\n",232src_x, src_y, src_w, src_h, info->format);233return -EINVAL;234}235236return 0;237}238239dma_addr_t240komeda_fb_get_pixel_addr(struct komeda_fb *kfb, int x, int y, int plane)241{242struct drm_framebuffer *fb = &kfb->base;243const struct drm_gem_dma_object *obj;244u32 offset, plane_x, plane_y, block_w, block_sz;245246if (plane >= fb->format->num_planes) {247DRM_DEBUG_KMS("Out of max plane num.\n");248return -EINVAL;249}250251obj = drm_fb_dma_get_gem_obj(fb, plane);252253offset = fb->offsets[plane];254if (!fb->modifier) {255block_w = drm_format_info_block_width(fb->format, plane);256block_sz = fb->format->char_per_block[plane];257plane_x = x / (plane ? fb->format->hsub : 1);258plane_y = y / (plane ? fb->format->vsub : 1);259260offset += (plane_x / block_w) * block_sz261+ plane_y * fb->pitches[plane];262}263264return obj->dma_addr + offset;265}266267/* if the fb can be supported by a specific layer */268bool komeda_fb_is_layer_supported(struct komeda_fb *kfb, u32 layer_type,269u32 rot)270{271struct drm_framebuffer *fb = &kfb->base;272struct komeda_dev *mdev = fb->dev->dev_private;273u32 fourcc = fb->format->format;274u64 modifier = fb->modifier;275bool supported;276277supported = komeda_format_mod_supported(&mdev->fmt_tbl, layer_type,278fourcc, modifier, rot);279if (!supported)280DRM_DEBUG_ATOMIC("Layer TYPE: %d doesn't support fb FMT: %p4cc with modifier: 0x%llx.\n",281layer_type, &fourcc, modifier);282283return supported;284}285286287