// SPDX-License-Identifier: MIT
/*
* Copyright 2022 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: AMD
*
*/
#include <drm/drm_atomic_helper.h>
#include <drm/drm_blend.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_plane_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_fourcc.h>
#include "amdgpu.h"
#include "dal_asic_id.h"
#include "amdgpu_display.h"
#include "amdgpu_dm_trace.h"
#include "amdgpu_dm_plane.h"
#include "gc/gc_11_0_0_offset.h"
#include "gc/gc_11_0_0_sh_mask.h"
/*
* TODO: these are currently initialized to rgb formats only.
* For future use cases we should either initialize them dynamically based on
* plane capabilities, or initialize this array to all formats, so internal drm
* check will succeed, and let DC implement proper check
*/
static const uint32_t rgb_formats[] = {
DRM_FORMAT_XRGB8888,
DRM_FORMAT_ARGB8888,
DRM_FORMAT_RGBA8888,
DRM_FORMAT_XRGB2101010,
DRM_FORMAT_XBGR2101010,
DRM_FORMAT_ARGB2101010,
DRM_FORMAT_ABGR2101010,
DRM_FORMAT_XRGB16161616,
DRM_FORMAT_XBGR16161616,
DRM_FORMAT_ARGB16161616,
DRM_FORMAT_ABGR16161616,
DRM_FORMAT_XBGR8888,
DRM_FORMAT_ABGR8888,
DRM_FORMAT_RGB565,
};
static const uint32_t overlay_formats[] = {
DRM_FORMAT_XRGB8888,
DRM_FORMAT_ARGB8888,
DRM_FORMAT_RGBA8888,
DRM_FORMAT_XBGR8888,
DRM_FORMAT_ABGR8888,
DRM_FORMAT_RGB565,
DRM_FORMAT_NV21,
DRM_FORMAT_NV12,
DRM_FORMAT_P010
};
static const uint32_t video_formats[] = {
DRM_FORMAT_NV21,
DRM_FORMAT_NV12,
DRM_FORMAT_P010
};
static const u32 cursor_formats[] = {
DRM_FORMAT_ARGB8888
};
enum dm_micro_swizzle {
MICRO_SWIZZLE_Z = 0,
MICRO_SWIZZLE_S = 1,
MICRO_SWIZZLE_D = 2,
MICRO_SWIZZLE_R = 3
};
const struct drm_format_info *amdgpu_dm_plane_get_format_info(const struct drm_mode_fb_cmd2 *cmd)
{
return amdgpu_lookup_format_info(cmd->pixel_format, cmd->modifier[0]);
}
void amdgpu_dm_plane_fill_blending_from_plane_state(const struct drm_plane_state *plane_state,
bool *per_pixel_alpha, bool *pre_multiplied_alpha,
bool *global_alpha, int *global_alpha_value)
{
*per_pixel_alpha = false;
*pre_multiplied_alpha = true;
*global_alpha = false;
*global_alpha_value = 0xff;
if (plane_state->plane->type != DRM_PLANE_TYPE_OVERLAY)
return;
if (plane_state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI ||
plane_state->pixel_blend_mode == DRM_MODE_BLEND_COVERAGE) {
static const uint32_t alpha_formats[] = {
DRM_FORMAT_ARGB8888,
DRM_FORMAT_RGBA8888,
DRM_FORMAT_ABGR8888,
DRM_FORMAT_ARGB2101010,
DRM_FORMAT_ABGR2101010,
DRM_FORMAT_ARGB16161616,
DRM_FORMAT_ABGR16161616,
DRM_FORMAT_ARGB16161616F,
};
uint32_t format = plane_state->fb->format->format;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(alpha_formats); ++i) {
if (format == alpha_formats[i]) {
*per_pixel_alpha = true;
break;
}
}
if (*per_pixel_alpha && plane_state->pixel_blend_mode == DRM_MODE_BLEND_COVERAGE)
*pre_multiplied_alpha = false;
}
if (plane_state->alpha < 0xffff) {
*global_alpha = true;
*global_alpha_value = plane_state->alpha >> 8;
}
}
static void add_modifier(uint64_t **mods, uint64_t *size, uint64_t *cap, uint64_t mod)
{
if (!*mods)
return;
if (*cap - *size < 1) {
uint64_t new_cap = *cap * 2;
uint64_t *new_mods = kmalloc(new_cap * sizeof(uint64_t), GFP_KERNEL);
if (!new_mods) {
kfree(*mods);
*mods = NULL;
return;
}
memcpy(new_mods, *mods, sizeof(uint64_t) * *size);
kfree(*mods);
*mods = new_mods;
*cap = new_cap;
}
(*mods)[*size] = mod;
*size += 1;
}
static bool modifier_has_dcc(uint64_t modifier)
{
return IS_AMD_FMT_MOD(modifier) && AMD_FMT_MOD_GET(DCC, modifier);
}
static unsigned int modifier_gfx9_swizzle_mode(uint64_t modifier)
{
if (modifier == DRM_FORMAT_MOD_LINEAR)
return 0;
return AMD_FMT_MOD_GET(TILE, modifier);
}
static void fill_gfx8_tiling_info_from_flags(union dc_tiling_info *tiling_info,
uint64_t tiling_flags)
{
/* Fill GFX8 params */
if (AMDGPU_TILING_GET(tiling_flags, ARRAY_MODE) == DC_ARRAY_2D_TILED_THIN1) {
unsigned int bankw, bankh, mtaspect, tile_split, num_banks;
bankw = AMDGPU_TILING_GET(tiling_flags, BANK_WIDTH);
bankh = AMDGPU_TILING_GET(tiling_flags, BANK_HEIGHT);
mtaspect = AMDGPU_TILING_GET(tiling_flags, MACRO_TILE_ASPECT);
tile_split = AMDGPU_TILING_GET(tiling_flags, TILE_SPLIT);
num_banks = AMDGPU_TILING_GET(tiling_flags, NUM_BANKS);
/* XXX fix me for VI */
tiling_info->gfx8.num_banks = num_banks;
tiling_info->gfx8.array_mode =
DC_ARRAY_2D_TILED_THIN1;
tiling_info->gfx8.tile_split = tile_split;
tiling_info->gfx8.bank_width = bankw;
tiling_info->gfx8.bank_height = bankh;
tiling_info->gfx8.tile_aspect = mtaspect;
tiling_info->gfx8.tile_mode =
DC_ADDR_SURF_MICRO_TILING_DISPLAY;
} else if (AMDGPU_TILING_GET(tiling_flags, ARRAY_MODE)
== DC_ARRAY_1D_TILED_THIN1) {
tiling_info->gfx8.array_mode = DC_ARRAY_1D_TILED_THIN1;
}
tiling_info->gfx8.pipe_config =
AMDGPU_TILING_GET(tiling_flags, PIPE_CONFIG);
}
static void fill_gfx9_tiling_info_from_device(const struct amdgpu_device *adev,
union dc_tiling_info *tiling_info)
{
/* Fill GFX9 params */
tiling_info->gfx9.num_pipes =
adev->gfx.config.gb_addr_config_fields.num_pipes;
tiling_info->gfx9.num_banks =
adev->gfx.config.gb_addr_config_fields.num_banks;
tiling_info->gfx9.pipe_interleave =
adev->gfx.config.gb_addr_con
|