162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * drm gem framebuffer helper functions
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2017 Noralf Trønnes
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/slab.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <drm/drm_damage_helper.h>
1262306a36Sopenharmony_ci#include <drm/drm_drv.h>
1362306a36Sopenharmony_ci#include <drm/drm_fourcc.h>
1462306a36Sopenharmony_ci#include <drm/drm_framebuffer.h>
1562306a36Sopenharmony_ci#include <drm/drm_gem.h>
1662306a36Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h>
1762306a36Sopenharmony_ci#include <drm/drm_modeset_helper.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "drm_internal.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ciMODULE_IMPORT_NS(DMA_BUF);
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define AFBC_HEADER_SIZE		16
2462306a36Sopenharmony_ci#define AFBC_TH_LAYOUT_ALIGNMENT	8
2562306a36Sopenharmony_ci#define AFBC_HDR_ALIGN			64
2662306a36Sopenharmony_ci#define AFBC_SUPERBLOCK_PIXELS		256
2762306a36Sopenharmony_ci#define AFBC_SUPERBLOCK_ALIGNMENT	128
2862306a36Sopenharmony_ci#define AFBC_TH_BODY_START_ALIGNMENT	4096
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/**
3162306a36Sopenharmony_ci * DOC: overview
3262306a36Sopenharmony_ci *
3362306a36Sopenharmony_ci * This library provides helpers for drivers that don't subclass
3462306a36Sopenharmony_ci * &drm_framebuffer and use &drm_gem_object for their backing storage.
3562306a36Sopenharmony_ci *
3662306a36Sopenharmony_ci * Drivers without additional needs to validate framebuffers can simply use
3762306a36Sopenharmony_ci * drm_gem_fb_create() and everything is wired up automatically. Other drivers
3862306a36Sopenharmony_ci * can use all parts independently.
3962306a36Sopenharmony_ci */
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/**
4262306a36Sopenharmony_ci * drm_gem_fb_get_obj() - Get GEM object backing the framebuffer
4362306a36Sopenharmony_ci * @fb: Framebuffer
4462306a36Sopenharmony_ci * @plane: Plane index
4562306a36Sopenharmony_ci *
4662306a36Sopenharmony_ci * No additional reference is taken beyond the one that the &drm_frambuffer
4762306a36Sopenharmony_ci * already holds.
4862306a36Sopenharmony_ci *
4962306a36Sopenharmony_ci * Returns:
5062306a36Sopenharmony_ci * Pointer to &drm_gem_object for the given framebuffer and plane index or NULL
5162306a36Sopenharmony_ci * if it does not exist.
5262306a36Sopenharmony_ci */
5362306a36Sopenharmony_cistruct drm_gem_object *drm_gem_fb_get_obj(struct drm_framebuffer *fb,
5462306a36Sopenharmony_ci					  unsigned int plane)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	struct drm_device *dev = fb->dev;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	if (drm_WARN_ON_ONCE(dev, plane >= ARRAY_SIZE(fb->obj)))
5962306a36Sopenharmony_ci		return NULL;
6062306a36Sopenharmony_ci	else if (drm_WARN_ON_ONCE(dev, !fb->obj[plane]))
6162306a36Sopenharmony_ci		return NULL;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	return fb->obj[plane];
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_gem_fb_get_obj);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic int
6862306a36Sopenharmony_cidrm_gem_fb_init(struct drm_device *dev,
6962306a36Sopenharmony_ci		 struct drm_framebuffer *fb,
7062306a36Sopenharmony_ci		 const struct drm_mode_fb_cmd2 *mode_cmd,
7162306a36Sopenharmony_ci		 struct drm_gem_object **obj, unsigned int num_planes,
7262306a36Sopenharmony_ci		 const struct drm_framebuffer_funcs *funcs)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	unsigned int i;
7562306a36Sopenharmony_ci	int ret;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	for (i = 0; i < num_planes; i++)
8062306a36Sopenharmony_ci		fb->obj[i] = obj[i];
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	ret = drm_framebuffer_init(dev, fb, funcs);
8362306a36Sopenharmony_ci	if (ret)
8462306a36Sopenharmony_ci		drm_err(dev, "Failed to init framebuffer: %d\n", ret);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	return ret;
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci/**
9062306a36Sopenharmony_ci * drm_gem_fb_destroy - Free GEM backed framebuffer
9162306a36Sopenharmony_ci * @fb: Framebuffer
9262306a36Sopenharmony_ci *
9362306a36Sopenharmony_ci * Frees a GEM backed framebuffer with its backing buffer(s) and the structure
9462306a36Sopenharmony_ci * itself. Drivers can use this as their &drm_framebuffer_funcs->destroy
9562306a36Sopenharmony_ci * callback.
9662306a36Sopenharmony_ci */
9762306a36Sopenharmony_civoid drm_gem_fb_destroy(struct drm_framebuffer *fb)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	unsigned int i;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	for (i = 0; i < fb->format->num_planes; i++)
10262306a36Sopenharmony_ci		drm_gem_object_put(fb->obj[i]);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	drm_framebuffer_cleanup(fb);
10562306a36Sopenharmony_ci	kfree(fb);
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ciEXPORT_SYMBOL(drm_gem_fb_destroy);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci/**
11062306a36Sopenharmony_ci * drm_gem_fb_create_handle - Create handle for GEM backed framebuffer
11162306a36Sopenharmony_ci * @fb: Framebuffer
11262306a36Sopenharmony_ci * @file: DRM file to register the handle for
11362306a36Sopenharmony_ci * @handle: Pointer to return the created handle
11462306a36Sopenharmony_ci *
11562306a36Sopenharmony_ci * This function creates a handle for the GEM object backing the framebuffer.
11662306a36Sopenharmony_ci * Drivers can use this as their &drm_framebuffer_funcs->create_handle
11762306a36Sopenharmony_ci * callback. The GETFB IOCTL calls into this callback.
11862306a36Sopenharmony_ci *
11962306a36Sopenharmony_ci * Returns:
12062306a36Sopenharmony_ci * 0 on success or a negative error code on failure.
12162306a36Sopenharmony_ci */
12262306a36Sopenharmony_ciint drm_gem_fb_create_handle(struct drm_framebuffer *fb, struct drm_file *file,
12362306a36Sopenharmony_ci			     unsigned int *handle)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	return drm_gem_handle_create(file, fb->obj[0], handle);
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ciEXPORT_SYMBOL(drm_gem_fb_create_handle);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci/**
13062306a36Sopenharmony_ci * drm_gem_fb_init_with_funcs() - Helper function for implementing
13162306a36Sopenharmony_ci *				  &drm_mode_config_funcs.fb_create
13262306a36Sopenharmony_ci *				  callback in cases when the driver
13362306a36Sopenharmony_ci *				  allocates a subclass of
13462306a36Sopenharmony_ci *				  struct drm_framebuffer
13562306a36Sopenharmony_ci * @dev: DRM device
13662306a36Sopenharmony_ci * @fb: framebuffer object
13762306a36Sopenharmony_ci * @file: DRM file that holds the GEM handle(s) backing the framebuffer
13862306a36Sopenharmony_ci * @mode_cmd: Metadata from the userspace framebuffer creation request
13962306a36Sopenharmony_ci * @funcs: vtable to be used for the new framebuffer object
14062306a36Sopenharmony_ci *
14162306a36Sopenharmony_ci * This function can be used to set &drm_framebuffer_funcs for drivers that need
14262306a36Sopenharmony_ci * custom framebuffer callbacks. Use drm_gem_fb_create() if you don't need to
14362306a36Sopenharmony_ci * change &drm_framebuffer_funcs. The function does buffer size validation.
14462306a36Sopenharmony_ci * The buffer size validation is for a general case, though, so users should
14562306a36Sopenharmony_ci * pay attention to the checks being appropriate for them or, at least,
14662306a36Sopenharmony_ci * non-conflicting.
14762306a36Sopenharmony_ci *
14862306a36Sopenharmony_ci * Returns:
14962306a36Sopenharmony_ci * Zero or a negative error code.
15062306a36Sopenharmony_ci */
15162306a36Sopenharmony_ciint drm_gem_fb_init_with_funcs(struct drm_device *dev,
15262306a36Sopenharmony_ci			       struct drm_framebuffer *fb,
15362306a36Sopenharmony_ci			       struct drm_file *file,
15462306a36Sopenharmony_ci			       const struct drm_mode_fb_cmd2 *mode_cmd,
15562306a36Sopenharmony_ci			       const struct drm_framebuffer_funcs *funcs)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	const struct drm_format_info *info;
15862306a36Sopenharmony_ci	struct drm_gem_object *objs[DRM_FORMAT_MAX_PLANES];
15962306a36Sopenharmony_ci	unsigned int i;
16062306a36Sopenharmony_ci	int ret;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	info = drm_get_format_info(dev, mode_cmd);
16362306a36Sopenharmony_ci	if (!info) {
16462306a36Sopenharmony_ci		drm_dbg_kms(dev, "Failed to get FB format info\n");
16562306a36Sopenharmony_ci		return -EINVAL;
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	if (drm_drv_uses_atomic_modeset(dev) &&
16962306a36Sopenharmony_ci	    !drm_any_plane_has_format(dev, mode_cmd->pixel_format,
17062306a36Sopenharmony_ci				      mode_cmd->modifier[0])) {
17162306a36Sopenharmony_ci		drm_dbg_kms(dev, "Unsupported pixel format %p4cc / modifier 0x%llx\n",
17262306a36Sopenharmony_ci			    &mode_cmd->pixel_format, mode_cmd->modifier[0]);
17362306a36Sopenharmony_ci		return -EINVAL;
17462306a36Sopenharmony_ci	}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	for (i = 0; i < info->num_planes; i++) {
17762306a36Sopenharmony_ci		unsigned int width = mode_cmd->width / (i ? info->hsub : 1);
17862306a36Sopenharmony_ci		unsigned int height = mode_cmd->height / (i ? info->vsub : 1);
17962306a36Sopenharmony_ci		unsigned int min_size;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci		objs[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]);
18262306a36Sopenharmony_ci		if (!objs[i]) {
18362306a36Sopenharmony_ci			drm_dbg_kms(dev, "Failed to lookup GEM object\n");
18462306a36Sopenharmony_ci			ret = -ENOENT;
18562306a36Sopenharmony_ci			goto err_gem_object_put;
18662306a36Sopenharmony_ci		}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci		min_size = (height - 1) * mode_cmd->pitches[i]
18962306a36Sopenharmony_ci			 + drm_format_info_min_pitch(info, i, width)
19062306a36Sopenharmony_ci			 + mode_cmd->offsets[i];
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci		if (objs[i]->size < min_size) {
19362306a36Sopenharmony_ci			drm_dbg_kms(dev,
19462306a36Sopenharmony_ci				    "GEM object size (%zu) smaller than minimum size (%u) for plane %d\n",
19562306a36Sopenharmony_ci				    objs[i]->size, min_size, i);
19662306a36Sopenharmony_ci			drm_gem_object_put(objs[i]);
19762306a36Sopenharmony_ci			ret = -EINVAL;
19862306a36Sopenharmony_ci			goto err_gem_object_put;
19962306a36Sopenharmony_ci		}
20062306a36Sopenharmony_ci	}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	ret = drm_gem_fb_init(dev, fb, mode_cmd, objs, i, funcs);
20362306a36Sopenharmony_ci	if (ret)
20462306a36Sopenharmony_ci		goto err_gem_object_put;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	return 0;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cierr_gem_object_put:
20962306a36Sopenharmony_ci	while (i > 0) {
21062306a36Sopenharmony_ci		--i;
21162306a36Sopenharmony_ci		drm_gem_object_put(objs[i]);
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci	return ret;
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_gem_fb_init_with_funcs);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci/**
21862306a36Sopenharmony_ci * drm_gem_fb_create_with_funcs() - Helper function for the
21962306a36Sopenharmony_ci *                                  &drm_mode_config_funcs.fb_create
22062306a36Sopenharmony_ci *                                  callback
22162306a36Sopenharmony_ci * @dev: DRM device
22262306a36Sopenharmony_ci * @file: DRM file that holds the GEM handle(s) backing the framebuffer
22362306a36Sopenharmony_ci * @mode_cmd: Metadata from the userspace framebuffer creation request
22462306a36Sopenharmony_ci * @funcs: vtable to be used for the new framebuffer object
22562306a36Sopenharmony_ci *
22662306a36Sopenharmony_ci * This function can be used to set &drm_framebuffer_funcs for drivers that need
22762306a36Sopenharmony_ci * custom framebuffer callbacks. Use drm_gem_fb_create() if you don't need to
22862306a36Sopenharmony_ci * change &drm_framebuffer_funcs. The function does buffer size validation.
22962306a36Sopenharmony_ci *
23062306a36Sopenharmony_ci * Returns:
23162306a36Sopenharmony_ci * Pointer to a &drm_framebuffer on success or an error pointer on failure.
23262306a36Sopenharmony_ci */
23362306a36Sopenharmony_cistruct drm_framebuffer *
23462306a36Sopenharmony_cidrm_gem_fb_create_with_funcs(struct drm_device *dev, struct drm_file *file,
23562306a36Sopenharmony_ci			     const struct drm_mode_fb_cmd2 *mode_cmd,
23662306a36Sopenharmony_ci			     const struct drm_framebuffer_funcs *funcs)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	struct drm_framebuffer *fb;
23962306a36Sopenharmony_ci	int ret;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	fb = kzalloc(sizeof(*fb), GFP_KERNEL);
24262306a36Sopenharmony_ci	if (!fb)
24362306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	ret = drm_gem_fb_init_with_funcs(dev, fb, file, mode_cmd, funcs);
24662306a36Sopenharmony_ci	if (ret) {
24762306a36Sopenharmony_ci		kfree(fb);
24862306a36Sopenharmony_ci		return ERR_PTR(ret);
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	return fb;
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_gem_fb_create_with_funcs);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_cistatic const struct drm_framebuffer_funcs drm_gem_fb_funcs = {
25662306a36Sopenharmony_ci	.destroy	= drm_gem_fb_destroy,
25762306a36Sopenharmony_ci	.create_handle	= drm_gem_fb_create_handle,
25862306a36Sopenharmony_ci};
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci/**
26162306a36Sopenharmony_ci * drm_gem_fb_create() - Helper function for the
26262306a36Sopenharmony_ci *                       &drm_mode_config_funcs.fb_create callback
26362306a36Sopenharmony_ci * @dev: DRM device
26462306a36Sopenharmony_ci * @file: DRM file that holds the GEM handle(s) backing the framebuffer
26562306a36Sopenharmony_ci * @mode_cmd: Metadata from the userspace framebuffer creation request
26662306a36Sopenharmony_ci *
26762306a36Sopenharmony_ci * This function creates a new framebuffer object described by
26862306a36Sopenharmony_ci * &drm_mode_fb_cmd2. This description includes handles for the buffer(s)
26962306a36Sopenharmony_ci * backing the framebuffer.
27062306a36Sopenharmony_ci *
27162306a36Sopenharmony_ci * If your hardware has special alignment or pitch requirements these should be
27262306a36Sopenharmony_ci * checked before calling this function. The function does buffer size
27362306a36Sopenharmony_ci * validation. Use drm_gem_fb_create_with_dirty() if you need framebuffer
27462306a36Sopenharmony_ci * flushing.
27562306a36Sopenharmony_ci *
27662306a36Sopenharmony_ci * Drivers can use this as their &drm_mode_config_funcs.fb_create callback.
27762306a36Sopenharmony_ci * The ADDFB2 IOCTL calls into this callback.
27862306a36Sopenharmony_ci *
27962306a36Sopenharmony_ci * Returns:
28062306a36Sopenharmony_ci * Pointer to a &drm_framebuffer on success or an error pointer on failure.
28162306a36Sopenharmony_ci */
28262306a36Sopenharmony_cistruct drm_framebuffer *
28362306a36Sopenharmony_cidrm_gem_fb_create(struct drm_device *dev, struct drm_file *file,
28462306a36Sopenharmony_ci		  const struct drm_mode_fb_cmd2 *mode_cmd)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	return drm_gem_fb_create_with_funcs(dev, file, mode_cmd,
28762306a36Sopenharmony_ci					    &drm_gem_fb_funcs);
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_gem_fb_create);
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_cistatic const struct drm_framebuffer_funcs drm_gem_fb_funcs_dirtyfb = {
29262306a36Sopenharmony_ci	.destroy	= drm_gem_fb_destroy,
29362306a36Sopenharmony_ci	.create_handle	= drm_gem_fb_create_handle,
29462306a36Sopenharmony_ci	.dirty		= drm_atomic_helper_dirtyfb,
29562306a36Sopenharmony_ci};
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci/**
29862306a36Sopenharmony_ci * drm_gem_fb_create_with_dirty() - Helper function for the
29962306a36Sopenharmony_ci *                       &drm_mode_config_funcs.fb_create callback
30062306a36Sopenharmony_ci * @dev: DRM device
30162306a36Sopenharmony_ci * @file: DRM file that holds the GEM handle(s) backing the framebuffer
30262306a36Sopenharmony_ci * @mode_cmd: Metadata from the userspace framebuffer creation request
30362306a36Sopenharmony_ci *
30462306a36Sopenharmony_ci * This function creates a new framebuffer object described by
30562306a36Sopenharmony_ci * &drm_mode_fb_cmd2. This description includes handles for the buffer(s)
30662306a36Sopenharmony_ci * backing the framebuffer. drm_atomic_helper_dirtyfb() is used for the dirty
30762306a36Sopenharmony_ci * callback giving framebuffer flushing through the atomic machinery. Use
30862306a36Sopenharmony_ci * drm_gem_fb_create() if you don't need the dirty callback.
30962306a36Sopenharmony_ci * The function does buffer size validation.
31062306a36Sopenharmony_ci *
31162306a36Sopenharmony_ci * Drivers should also call drm_plane_enable_fb_damage_clips() on all planes
31262306a36Sopenharmony_ci * to enable userspace to use damage clips also with the ATOMIC IOCTL.
31362306a36Sopenharmony_ci *
31462306a36Sopenharmony_ci * Drivers can use this as their &drm_mode_config_funcs.fb_create callback.
31562306a36Sopenharmony_ci * The ADDFB2 IOCTL calls into this callback.
31662306a36Sopenharmony_ci *
31762306a36Sopenharmony_ci * Returns:
31862306a36Sopenharmony_ci * Pointer to a &drm_framebuffer on success or an error pointer on failure.
31962306a36Sopenharmony_ci */
32062306a36Sopenharmony_cistruct drm_framebuffer *
32162306a36Sopenharmony_cidrm_gem_fb_create_with_dirty(struct drm_device *dev, struct drm_file *file,
32262306a36Sopenharmony_ci			     const struct drm_mode_fb_cmd2 *mode_cmd)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	return drm_gem_fb_create_with_funcs(dev, file, mode_cmd,
32562306a36Sopenharmony_ci					    &drm_gem_fb_funcs_dirtyfb);
32662306a36Sopenharmony_ci}
32762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_gem_fb_create_with_dirty);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci/**
33062306a36Sopenharmony_ci * drm_gem_fb_vmap - maps all framebuffer BOs into kernel address space
33162306a36Sopenharmony_ci * @fb: the framebuffer
33262306a36Sopenharmony_ci * @map: returns the mapping's address for each BO
33362306a36Sopenharmony_ci * @data: returns the data address for each BO, can be NULL
33462306a36Sopenharmony_ci *
33562306a36Sopenharmony_ci * This function maps all buffer objects of the given framebuffer into
33662306a36Sopenharmony_ci * kernel address space and stores them in struct iosys_map. If the
33762306a36Sopenharmony_ci * mapping operation fails for one of the BOs, the function unmaps the
33862306a36Sopenharmony_ci * already established mappings automatically.
33962306a36Sopenharmony_ci *
34062306a36Sopenharmony_ci * Callers that want to access a BO's stored data should pass @data.
34162306a36Sopenharmony_ci * The argument returns the addresses of the data stored in each BO. This
34262306a36Sopenharmony_ci * is different from @map if the framebuffer's offsets field is non-zero.
34362306a36Sopenharmony_ci *
34462306a36Sopenharmony_ci * Both, @map and @data, must each refer to arrays with at least
34562306a36Sopenharmony_ci * fb->format->num_planes elements.
34662306a36Sopenharmony_ci *
34762306a36Sopenharmony_ci * See drm_gem_fb_vunmap() for unmapping.
34862306a36Sopenharmony_ci *
34962306a36Sopenharmony_ci * Returns:
35062306a36Sopenharmony_ci * 0 on success, or a negative errno code otherwise.
35162306a36Sopenharmony_ci */
35262306a36Sopenharmony_ciint drm_gem_fb_vmap(struct drm_framebuffer *fb, struct iosys_map *map,
35362306a36Sopenharmony_ci		    struct iosys_map *data)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	struct drm_gem_object *obj;
35662306a36Sopenharmony_ci	unsigned int i;
35762306a36Sopenharmony_ci	int ret;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	for (i = 0; i < fb->format->num_planes; ++i) {
36062306a36Sopenharmony_ci		obj = drm_gem_fb_get_obj(fb, i);
36162306a36Sopenharmony_ci		if (!obj) {
36262306a36Sopenharmony_ci			ret = -EINVAL;
36362306a36Sopenharmony_ci			goto err_drm_gem_vunmap;
36462306a36Sopenharmony_ci		}
36562306a36Sopenharmony_ci		ret = drm_gem_vmap_unlocked(obj, &map[i]);
36662306a36Sopenharmony_ci		if (ret)
36762306a36Sopenharmony_ci			goto err_drm_gem_vunmap;
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	if (data) {
37162306a36Sopenharmony_ci		for (i = 0; i < fb->format->num_planes; ++i) {
37262306a36Sopenharmony_ci			memcpy(&data[i], &map[i], sizeof(data[i]));
37362306a36Sopenharmony_ci			if (iosys_map_is_null(&data[i]))
37462306a36Sopenharmony_ci				continue;
37562306a36Sopenharmony_ci			iosys_map_incr(&data[i], fb->offsets[i]);
37662306a36Sopenharmony_ci		}
37762306a36Sopenharmony_ci	}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	return 0;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_cierr_drm_gem_vunmap:
38262306a36Sopenharmony_ci	while (i) {
38362306a36Sopenharmony_ci		--i;
38462306a36Sopenharmony_ci		obj = drm_gem_fb_get_obj(fb, i);
38562306a36Sopenharmony_ci		if (!obj)
38662306a36Sopenharmony_ci			continue;
38762306a36Sopenharmony_ci		drm_gem_vunmap_unlocked(obj, &map[i]);
38862306a36Sopenharmony_ci	}
38962306a36Sopenharmony_ci	return ret;
39062306a36Sopenharmony_ci}
39162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_gem_fb_vmap);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci/**
39462306a36Sopenharmony_ci * drm_gem_fb_vunmap - unmaps framebuffer BOs from kernel address space
39562306a36Sopenharmony_ci * @fb: the framebuffer
39662306a36Sopenharmony_ci * @map: mapping addresses as returned by drm_gem_fb_vmap()
39762306a36Sopenharmony_ci *
39862306a36Sopenharmony_ci * This function unmaps all buffer objects of the given framebuffer.
39962306a36Sopenharmony_ci *
40062306a36Sopenharmony_ci * See drm_gem_fb_vmap() for more information.
40162306a36Sopenharmony_ci */
40262306a36Sopenharmony_civoid drm_gem_fb_vunmap(struct drm_framebuffer *fb, struct iosys_map *map)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	unsigned int i = fb->format->num_planes;
40562306a36Sopenharmony_ci	struct drm_gem_object *obj;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	while (i) {
40862306a36Sopenharmony_ci		--i;
40962306a36Sopenharmony_ci		obj = drm_gem_fb_get_obj(fb, i);
41062306a36Sopenharmony_ci		if (!obj)
41162306a36Sopenharmony_ci			continue;
41262306a36Sopenharmony_ci		if (iosys_map_is_null(&map[i]))
41362306a36Sopenharmony_ci			continue;
41462306a36Sopenharmony_ci		drm_gem_vunmap_unlocked(obj, &map[i]);
41562306a36Sopenharmony_ci	}
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ciEXPORT_SYMBOL(drm_gem_fb_vunmap);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_cistatic void __drm_gem_fb_end_cpu_access(struct drm_framebuffer *fb, enum dma_data_direction dir,
42062306a36Sopenharmony_ci					unsigned int num_planes)
42162306a36Sopenharmony_ci{
42262306a36Sopenharmony_ci	struct dma_buf_attachment *import_attach;
42362306a36Sopenharmony_ci	struct drm_gem_object *obj;
42462306a36Sopenharmony_ci	int ret;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	while (num_planes) {
42762306a36Sopenharmony_ci		--num_planes;
42862306a36Sopenharmony_ci		obj = drm_gem_fb_get_obj(fb, num_planes);
42962306a36Sopenharmony_ci		if (!obj)
43062306a36Sopenharmony_ci			continue;
43162306a36Sopenharmony_ci		import_attach = obj->import_attach;
43262306a36Sopenharmony_ci		if (!import_attach)
43362306a36Sopenharmony_ci			continue;
43462306a36Sopenharmony_ci		ret = dma_buf_end_cpu_access(import_attach->dmabuf, dir);
43562306a36Sopenharmony_ci		if (ret)
43662306a36Sopenharmony_ci			drm_err(fb->dev, "dma_buf_end_cpu_access(%u, %d) failed: %d\n",
43762306a36Sopenharmony_ci				ret, num_planes, dir);
43862306a36Sopenharmony_ci	}
43962306a36Sopenharmony_ci}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci/**
44262306a36Sopenharmony_ci * drm_gem_fb_begin_cpu_access - prepares GEM buffer objects for CPU access
44362306a36Sopenharmony_ci * @fb: the framebuffer
44462306a36Sopenharmony_ci * @dir: access mode
44562306a36Sopenharmony_ci *
44662306a36Sopenharmony_ci * Prepares a framebuffer's GEM buffer objects for CPU access. This function
44762306a36Sopenharmony_ci * must be called before accessing the BO data within the kernel. For imported
44862306a36Sopenharmony_ci * BOs, the function calls dma_buf_begin_cpu_access().
44962306a36Sopenharmony_ci *
45062306a36Sopenharmony_ci * See drm_gem_fb_end_cpu_access() for signalling the end of CPU access.
45162306a36Sopenharmony_ci *
45262306a36Sopenharmony_ci * Returns:
45362306a36Sopenharmony_ci * 0 on success, or a negative errno code otherwise.
45462306a36Sopenharmony_ci */
45562306a36Sopenharmony_ciint drm_gem_fb_begin_cpu_access(struct drm_framebuffer *fb, enum dma_data_direction dir)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	struct dma_buf_attachment *import_attach;
45862306a36Sopenharmony_ci	struct drm_gem_object *obj;
45962306a36Sopenharmony_ci	unsigned int i;
46062306a36Sopenharmony_ci	int ret;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	for (i = 0; i < fb->format->num_planes; ++i) {
46362306a36Sopenharmony_ci		obj = drm_gem_fb_get_obj(fb, i);
46462306a36Sopenharmony_ci		if (!obj) {
46562306a36Sopenharmony_ci			ret = -EINVAL;
46662306a36Sopenharmony_ci			goto err___drm_gem_fb_end_cpu_access;
46762306a36Sopenharmony_ci		}
46862306a36Sopenharmony_ci		import_attach = obj->import_attach;
46962306a36Sopenharmony_ci		if (!import_attach)
47062306a36Sopenharmony_ci			continue;
47162306a36Sopenharmony_ci		ret = dma_buf_begin_cpu_access(import_attach->dmabuf, dir);
47262306a36Sopenharmony_ci		if (ret)
47362306a36Sopenharmony_ci			goto err___drm_gem_fb_end_cpu_access;
47462306a36Sopenharmony_ci	}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	return 0;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_cierr___drm_gem_fb_end_cpu_access:
47962306a36Sopenharmony_ci	__drm_gem_fb_end_cpu_access(fb, dir, i);
48062306a36Sopenharmony_ci	return ret;
48162306a36Sopenharmony_ci}
48262306a36Sopenharmony_ciEXPORT_SYMBOL(drm_gem_fb_begin_cpu_access);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci/**
48562306a36Sopenharmony_ci * drm_gem_fb_end_cpu_access - signals end of CPU access to GEM buffer objects
48662306a36Sopenharmony_ci * @fb: the framebuffer
48762306a36Sopenharmony_ci * @dir: access mode
48862306a36Sopenharmony_ci *
48962306a36Sopenharmony_ci * Signals the end of CPU access to the given framebuffer's GEM buffer objects. This
49062306a36Sopenharmony_ci * function must be paired with a corresponding call to drm_gem_fb_begin_cpu_access().
49162306a36Sopenharmony_ci * For imported BOs, the function calls dma_buf_end_cpu_access().
49262306a36Sopenharmony_ci *
49362306a36Sopenharmony_ci * See also drm_gem_fb_begin_cpu_access().
49462306a36Sopenharmony_ci */
49562306a36Sopenharmony_civoid drm_gem_fb_end_cpu_access(struct drm_framebuffer *fb, enum dma_data_direction dir)
49662306a36Sopenharmony_ci{
49762306a36Sopenharmony_ci	__drm_gem_fb_end_cpu_access(fb, dir, fb->format->num_planes);
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ciEXPORT_SYMBOL(drm_gem_fb_end_cpu_access);
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci// TODO Drop this function and replace by drm_format_info_bpp() once all
50262306a36Sopenharmony_ci// DRM_FORMAT_* provide proper block info in drivers/gpu/drm/drm_fourcc.c
50362306a36Sopenharmony_cistatic __u32 drm_gem_afbc_get_bpp(struct drm_device *dev,
50462306a36Sopenharmony_ci				  const struct drm_mode_fb_cmd2 *mode_cmd)
50562306a36Sopenharmony_ci{
50662306a36Sopenharmony_ci	const struct drm_format_info *info;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	info = drm_get_format_info(dev, mode_cmd);
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	switch (info->format) {
51162306a36Sopenharmony_ci	case DRM_FORMAT_YUV420_8BIT:
51262306a36Sopenharmony_ci		return 12;
51362306a36Sopenharmony_ci	case DRM_FORMAT_YUV420_10BIT:
51462306a36Sopenharmony_ci		return 15;
51562306a36Sopenharmony_ci	case DRM_FORMAT_VUY101010:
51662306a36Sopenharmony_ci		return 30;
51762306a36Sopenharmony_ci	default:
51862306a36Sopenharmony_ci		return drm_format_info_bpp(info, 0);
51962306a36Sopenharmony_ci	}
52062306a36Sopenharmony_ci}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_cistatic int drm_gem_afbc_min_size(struct drm_device *dev,
52362306a36Sopenharmony_ci				 const struct drm_mode_fb_cmd2 *mode_cmd,
52462306a36Sopenharmony_ci				 struct drm_afbc_framebuffer *afbc_fb)
52562306a36Sopenharmony_ci{
52662306a36Sopenharmony_ci	__u32 n_blocks, w_alignment, h_alignment, hdr_alignment;
52762306a36Sopenharmony_ci	/* remove bpp when all users properly encode cpp in drm_format_info */
52862306a36Sopenharmony_ci	__u32 bpp;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	switch (mode_cmd->modifier[0] & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK) {
53162306a36Sopenharmony_ci	case AFBC_FORMAT_MOD_BLOCK_SIZE_16x16:
53262306a36Sopenharmony_ci		afbc_fb->block_width = 16;
53362306a36Sopenharmony_ci		afbc_fb->block_height = 16;
53462306a36Sopenharmony_ci		break;
53562306a36Sopenharmony_ci	case AFBC_FORMAT_MOD_BLOCK_SIZE_32x8:
53662306a36Sopenharmony_ci		afbc_fb->block_width = 32;
53762306a36Sopenharmony_ci		afbc_fb->block_height = 8;
53862306a36Sopenharmony_ci		break;
53962306a36Sopenharmony_ci	/* no user exists yet - fall through */
54062306a36Sopenharmony_ci	case AFBC_FORMAT_MOD_BLOCK_SIZE_64x4:
54162306a36Sopenharmony_ci	case AFBC_FORMAT_MOD_BLOCK_SIZE_32x8_64x4:
54262306a36Sopenharmony_ci	default:
54362306a36Sopenharmony_ci		drm_dbg_kms(dev, "Invalid AFBC_FORMAT_MOD_BLOCK_SIZE: %lld.\n",
54462306a36Sopenharmony_ci			    mode_cmd->modifier[0]
54562306a36Sopenharmony_ci			    & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK);
54662306a36Sopenharmony_ci		return -EINVAL;
54762306a36Sopenharmony_ci	}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	/* tiled header afbc */
55062306a36Sopenharmony_ci	w_alignment = afbc_fb->block_width;
55162306a36Sopenharmony_ci	h_alignment = afbc_fb->block_height;
55262306a36Sopenharmony_ci	hdr_alignment = AFBC_HDR_ALIGN;
55362306a36Sopenharmony_ci	if (mode_cmd->modifier[0] & AFBC_FORMAT_MOD_TILED) {
55462306a36Sopenharmony_ci		w_alignment *= AFBC_TH_LAYOUT_ALIGNMENT;
55562306a36Sopenharmony_ci		h_alignment *= AFBC_TH_LAYOUT_ALIGNMENT;
55662306a36Sopenharmony_ci		hdr_alignment = AFBC_TH_BODY_START_ALIGNMENT;
55762306a36Sopenharmony_ci	}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	afbc_fb->aligned_width = ALIGN(mode_cmd->width, w_alignment);
56062306a36Sopenharmony_ci	afbc_fb->aligned_height = ALIGN(mode_cmd->height, h_alignment);
56162306a36Sopenharmony_ci	afbc_fb->offset = mode_cmd->offsets[0];
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	bpp = drm_gem_afbc_get_bpp(dev, mode_cmd);
56462306a36Sopenharmony_ci	if (!bpp) {
56562306a36Sopenharmony_ci		drm_dbg_kms(dev, "Invalid AFBC bpp value: %d\n", bpp);
56662306a36Sopenharmony_ci		return -EINVAL;
56762306a36Sopenharmony_ci	}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	n_blocks = (afbc_fb->aligned_width * afbc_fb->aligned_height)
57062306a36Sopenharmony_ci		   / AFBC_SUPERBLOCK_PIXELS;
57162306a36Sopenharmony_ci	afbc_fb->afbc_size = ALIGN(n_blocks * AFBC_HEADER_SIZE, hdr_alignment);
57262306a36Sopenharmony_ci	afbc_fb->afbc_size += n_blocks * ALIGN(bpp * AFBC_SUPERBLOCK_PIXELS / 8,
57362306a36Sopenharmony_ci					       AFBC_SUPERBLOCK_ALIGNMENT);
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	return 0;
57662306a36Sopenharmony_ci}
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci/**
57962306a36Sopenharmony_ci * drm_gem_fb_afbc_init() - Helper function for drivers using afbc to
58062306a36Sopenharmony_ci *			    fill and validate all the afbc-specific
58162306a36Sopenharmony_ci *			    struct drm_afbc_framebuffer members
58262306a36Sopenharmony_ci *
58362306a36Sopenharmony_ci * @dev: DRM device
58462306a36Sopenharmony_ci * @afbc_fb: afbc-specific framebuffer
58562306a36Sopenharmony_ci * @mode_cmd: Metadata from the userspace framebuffer creation request
58662306a36Sopenharmony_ci * @afbc_fb: afbc framebuffer
58762306a36Sopenharmony_ci *
58862306a36Sopenharmony_ci * This function can be used by drivers which support afbc to complete
58962306a36Sopenharmony_ci * the preparation of struct drm_afbc_framebuffer. It must be called after
59062306a36Sopenharmony_ci * allocating the said struct and calling drm_gem_fb_init_with_funcs().
59162306a36Sopenharmony_ci * It is caller's responsibility to put afbc_fb->base.obj objects in case
59262306a36Sopenharmony_ci * the call is unsuccessful.
59362306a36Sopenharmony_ci *
59462306a36Sopenharmony_ci * Returns:
59562306a36Sopenharmony_ci * Zero on success or a negative error value on failure.
59662306a36Sopenharmony_ci */
59762306a36Sopenharmony_ciint drm_gem_fb_afbc_init(struct drm_device *dev,
59862306a36Sopenharmony_ci			 const struct drm_mode_fb_cmd2 *mode_cmd,
59962306a36Sopenharmony_ci			 struct drm_afbc_framebuffer *afbc_fb)
60062306a36Sopenharmony_ci{
60162306a36Sopenharmony_ci	const struct drm_format_info *info;
60262306a36Sopenharmony_ci	struct drm_gem_object **objs;
60362306a36Sopenharmony_ci	int ret;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	objs = afbc_fb->base.obj;
60662306a36Sopenharmony_ci	info = drm_get_format_info(dev, mode_cmd);
60762306a36Sopenharmony_ci	if (!info)
60862306a36Sopenharmony_ci		return -EINVAL;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	ret = drm_gem_afbc_min_size(dev, mode_cmd, afbc_fb);
61162306a36Sopenharmony_ci	if (ret < 0)
61262306a36Sopenharmony_ci		return ret;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	if (objs[0]->size < afbc_fb->afbc_size)
61562306a36Sopenharmony_ci		return -EINVAL;
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	return 0;
61862306a36Sopenharmony_ci}
61962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_gem_fb_afbc_init);
620