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