Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362// SPDX-License-Identifier: MIT/* * Copyright 2022-2026 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_framebuffer.h"#include <drm/drm_gem_atomic_helper.h>#include <drm/drm_panic_helper.h>#include <drm/drm_plane_helper.h>#include <drm/drm_gem_framebuffer_helper.h>#include <drm/drm_fourcc.h>
#include "amdgpu.h"#include "dc.h"#include "dal_asic_id.h"#include "amdgpu_display.h"#include "amdgpu_dm_trace.h"#include "amdgpu_dm_plane.h"#include "dm_helpers.h"#include "amdgpu_dm_colorop.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(u32 pixel_format, u64 modifier){ return amdgpu_lookup_format_info(pixel_format, modifier);}EXPORT_IF_KUNIT(amdgpu_dm_plane_get_format_info);
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->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI || plane_state->pixel_blend_mode == DRM_MODE_BLEND_COVERAGE) { *per_pixel_alpha = plane_state->fb->format->has_alpha;
if (*per_pixel_alpha && plane_state->pixel_blend_mode == DRM_MODE_BLEND_COVERAGE) *pre_multiplied_alpha = false; }
if (plane_state->alpha < 0xffff) { struct amdgpu_device *adev = drm_to_adev(plane_state->plane->dev); *global_alpha = true; /* * DCN 4.2 uses a 12-bit MPCC_GLOBAL_ALPHA field, while * other ASICs use an 8-bit field. The DRM plane alpha is * 16-bit, so scale it down to the width the hardware expects. */ if (amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 2, 0) || amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 2, 1) || amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(6, 0, 0)) *global_alpha_value = plane_state->alpha >> 4; else *global_alpha_value = plane_state->alpha >> 8;
}}EXPORT_IF_KUNIT(amdgpu_dm_plane_fill_blending_from_plane_state);
STATIC_IFN_KUNIT void amdgpu_dm_plane_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_array(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;}EXPORT_IF_KUNIT(amdgpu_dm_plane_add_modifier);
STATIC_IFN_KUNIT void amdgpu_dm_plane_add_modifier_dedup(uint64_t **mods, uint64_t *size, uint64_t *cap, uint64_t mod){ uint64_t i;
if (!*mods) return;
for (i = 0; i < *size; ++i) if ((*mods)[i] == mod) return;
amdgpu_dm_plane_add_modifier(mods, size, cap, mod);}EXPORT_IF_KUNIT(amdgpu_dm_plane_add_modifier_dedup);
STATIC_IFN_KUNIT bool amdgpu_dm_plane_modifier_has_dcc(uint64_t modifier){ return IS_AMD_FMT_MOD(modifier) && AMD_FMT_MOD_GET(DCC, modifier);}EXPORT_IF_KUNIT(amdgpu_dm_plane_modifier_has_dcc);
STATIC_IFN_KUNIT unsigned int amdgpu_dm_plane_modifier_gfx9_swizzle_mode(uint64_t modifier){ if (modifier == DRM_FORMAT_MOD_LINEAR) return 0;
return AMD_FMT_MOD_GET(TILE, modifier);}EXPORT_IF_KUNIT(amdgpu_dm_plane_modifier_gfx9_swizzle_mode);
STATIC_IFN_KUNIT int amdgpu_dm_plane_fill_gfx6_tiling_info_from_modifier( struct dc_tiling_info *tiling_info, uint64_t modifier){ if (modifier == DRM_FORMAT_MOD_LINEAR) { tiling_info->gfx8.array_mode = DC_ARRAY_LINEAR_GENERAL; tiling_info->gfxversion = DcGfxVersion8;
return 0; }
if (!IS_AMD_FMT_MOD(modifier)) return -EINVAL;
if (AMD_FMT_MOD_GET(TILE_VERSION, modifier) != AMD_FMT_MOD_TILE_VER_GFX6) return -EINVAL;
tiling_info->gfx8.array_mode = AMD_FMT_MOD_GET(TILE, modifier); tiling_info->gfxversion = DcGfxVersion8; tiling_info->gfx8.tile_mode = AMD_FMT_MOD_GET(MICROTILE, modifier);
if (AMD_FMT_MOD_GET(TILE, modifier) < AMD_FMT_MOD_TILE_GFX6_2D_TILED_THIN1) return 0;
tiling_info->gfx8.pipe_config = AMD_FMT_MOD_GET(PIPE_CONFIG, modifier); tiling_info->gfx8.tile_split = AMD_FMT_MOD_GET(TILE_SPLIT, modifier); tiling_info->gfx8.bank_width = AMD_FMT_MOD_GET(BANK_WIDTH, modifier); tiling_info->gfx8.bank_height = AMD_FMT_MOD_GET(BANK_HEIGHT, modifier); tiling_info->gfx8.tile_aspect = AMD_FMT_MOD_GET(MACRO_TILE_ASPECT, modifier); tiling_info->gfx8.num_banks = AMD_FMT_MOD_GET(NUM_BANKS, modifier);
return 0;}EXPORT_IF_KUNIT(amdgpu_dm_plane_fill_gfx6_tiling_info_from_modifier);
STATIC_IFN_KUNIT void amdgpu_dm_plane_fill_gfx9_tiling_info_from_device(const struct amdgpu_device *adev, struct 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_config_fields.pipe_interleave_size; tiling_info->gfx9.num_shader_engines = adev->gfx.config.gb_addr_config_fields.num_se; tiling_info->gfx9.max_compressed_frags = adev->gfx.config.gb_addr_config_fields.max_compress_frags; tiling_info->gfx9.num_rb_per_se = adev->gfx.config.gb_addr_config_fields.num_rb_per_se; tiling_info->gfx9.shaderEnable = 1; if (amdgpu_ip_version(adev, GC_HWIP, 0) >= IP_VERSION(10, 3, 0)) tiling_info->gfx9.num_pkrs = adev->gfx.config.gb_addr_config_fields.num_pkrs;}EXPORT_IF_KUNIT(amdgpu_dm_plane_fill_gfx9_tiling_info_from_device);
STATIC_IFN_KUNIT void amdgpu_dm_plane_fill_gfx9_tiling_info_from_modifier(const struct amdgpu_device *adev, struct dc_tiling_info *tiling_info, uint64_t modifier){ unsigned int mod_bank_xor_bits = AMD_FMT_MOD_GET(BANK_XOR_BITS, modifier); unsigned int mod_pipe_xor_bits = AMD_FMT_MOD_GET(PIPE_XOR_BITS, modifier); unsigned int pkrs_log2 = AMD_FMT_MOD_GET(PACKERS, modifier); unsigned int pipes_log2;
pipes_log2 = min(5u, mod_pipe_xor_bits);
amdgpu_dm_plane_fill_gfx9_tiling_info_from_device(adev, tiling_info);
if (!IS_AMD_FMT_MOD(modifier)) return;
tiling_info->gfx9.num_pipes = 1u << pipes_log2; tiling_info->gfx9.num_shader_engines = 1u << (mod_pipe_xor_bits - pipes_log2);
if (adev->family >= AMDGPU_FAMILY_NV) { tiling_info->gfx9.num_pkrs = 1u << pkrs_log2; } else { tiling_info->gfx9.num_banks = 1u << mod_bank_xor_bits;
/* for DCC we know it isn't rb aligned, so rb_per_se doesn't matter. */ }}EXPORT_IF_KUNIT(amdgpu_dm_plane_fill_gfx9_tiling_info_from_modifier);
STATIC_IFN_KUNIT int amdgpu_dm_plane_validate_dcc(struct amdgpu_device *adev, const enum surface_pixel_format format, const enum dc_rotation_angle rotation, const struct dc_tiling_info *tiling_info, const struct dc_plane_dcc_param *dcc, const struct dc_plane_address *address, const struct plane_size *plane_size){ struct dc *dc = adev->dm.dc; struct dc_dcc_surface_param input; struct dc_surface_dcc_cap output;
memset(&input, 0, sizeof(input)); memset(&output, 0, sizeof(output));
if (!dcc->enable) return 0;
if (adev->family != AMDGPU_FAMILY_GC_12_0_0 && adev->family != AMDGPU_FAMILY_GC_13_0_1 && format >= SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) return -EINVAL;
if (!dc->cap_funcs.get_dcc_compression_cap) return -EINVAL;
input.format = format; input.surface_size.width = plane_size->surface_size.width; input.surface_size.height = plane_size->surface_size.height; input.swizzle_mode = tiling_info->gfx9.swizzle;
if (rotation == ROTATION_ANGLE_0 || rotation == ROTATION_ANGLE_180) input.scan = SCAN_DIRECTION_HORIZONTAL; else if (rotation == ROTATION_ANGLE_90 || rotation == ROTATION_ANGLE_270) input.scan = SCAN_DIRECTION_VERTICAL;
if (!dc->cap_funcs.get_dcc_compression_cap(dc, &input, &output)) return -EINVAL;
if (!output.capable) return -EINVAL;
if (dcc->independent_64b_blks == 0 && output.grph.rgb.independent_64b_blks != 0) return -EINVAL;
return 0;}EXPORT_IF_KUNIT(amdgpu_dm_plane_validate_dcc);
STATIC_IFN_KUNIT int amdgpu_dm_plane_fill_gfx9_attrs_from_modifiers(struct amdgpu_device *adev, const struct amdgpu_framebuffer *afb, const enum surface_pixel_format format, const enum dc_rotation_angle rotation, const struct plane_size *plane_size, struct dc_tiling_info *tiling_info, struct dc_plane_dcc_param *dcc, struct dc_plane_address *address){ const uint64_t modifier = afb->base.modifier; int ret = 0;
amdgpu_dm_plane_fill_gfx9_tiling_info_from_modifier(adev, tiling_info, modifier); tiling_info->gfx9.swizzle = amdgpu_dm_plane_modifier_gfx9_swizzle_mode(modifier); tiling_info->gfxversion = DcGfxVersion9;
if (amdgpu_dm_plane_modifier_has_dcc(modifier)) { uint64_t dcc_address = afb->address + afb->base.offsets[1]; bool independent_64b_blks = AMD_FMT_MOD_GET(DCC_INDEPENDENT_64B, modifier); bool independent_128b_blks = AMD_FMT_MOD_GET(DCC_INDEPENDENT_128B, modifier);
dcc->enable = 1; dcc->meta_pitch = afb->base.pitches[1]; dcc->independent_64b_blks = independent_64b_blks; if (AMD_FMT_MOD_GET(TILE_VERSION, modifier) >= AMD_FMT_MOD_TILE_VER_GFX10_RBPLUS) { if (independent_64b_blks && independent_128b_blks) dcc->dcc_ind_blk = hubp_ind_block_64b_no_128bcl; else if (independent_128b_blks) dcc->dcc_ind_blk = hubp_ind_block_128b; else if (independent_64b_blks && !independent_128b_blks) dcc->dcc_ind_blk = hubp_ind_block_64b; else dcc->dcc_ind_blk = hubp_ind_block_unconstrained; } else { if (independent_64b_blks) dcc->dcc_ind_blk = hubp_ind_block_64b; else dcc->dcc_ind_blk = hubp_ind_block_unconstrained; }
address->grph.meta_addr.low_part = lower_32_bits(dcc_address); address->grph.meta_addr.high_part = upper_32_bits(dcc_address); }
ret = amdgpu_dm_plane_validate_dcc(adev, format, rotation, tiling_info, dcc, address, plane_size); if (ret) drm_dbg_kms(adev_to_drm(adev), "amdgpu_dm_plane_validate_dcc: returned error: %pe\n", ERR_PTR(ret));
return ret;}EXPORT_IF_KUNIT(amdgpu_dm_plane_fill_gfx9_attrs_from_modifiers);
STATIC_IFN_KUNIT int amdgpu_dm_plane_fill_gfx12_attrs_from_modifiers(struct amdgpu_device *adev, const struct amdgpu_framebuffer *afb, const enum surface_pixel_format format, const enum dc_rotation_angle rotation, const struct plane_size *plane_size, struct dc_tiling_info *tiling_info, struct dc_plane_dcc_param *dcc, struct dc_plane_address *address){ const uint64_t modifier = afb->base.modifier; int ret = 0;
/* TODO: Most of this function shouldn't be needed on GFX12. */ amdgpu_dm_plane_fill_gfx9_tiling_info_from_device(adev, tiling_info);
tiling_info->gfx9.swizzle = amdgpu_dm_plane_modifier_gfx9_swizzle_mode(modifier); tiling_info->gfxversion = DcGfxAddr3;
if (amdgpu_dm_plane_modifier_has_dcc(modifier)) { int max_compressed_block = AMD_FMT_MOD_GET(DCC_MAX_COMPRESSED_BLOCK, modifier);
dcc->enable = 1; dcc->independent_64b_blks = max_compressed_block == 0;
if (max_compressed_block == 0) dcc->dcc_ind_blk = hubp_ind_block_64b; else if (max_compressed_block == 1) dcc->dcc_ind_blk = hubp_ind_block_128b; else dcc->dcc_ind_blk = hubp_ind_block_unconstrained; }
/* TODO: This seems wrong because there is no DCC plane on GFX12. */ ret = amdgpu_dm_plane_validate_dcc(adev, format, rotation, tiling_info, dcc, address, plane_size); if (ret) drm_dbg_kms(adev_to_drm(adev), "amdgpu_dm_plane_validate_dcc: returned: %pe\n", ERR_PTR(ret));
return ret;}EXPORT_IF_KUNIT(amdgpu_dm_plane_fill_gfx12_attrs_from_modifiers);
static void amdgpu_dm_plane_add_gfx10_1_modifiers(const struct amdgpu_device *adev, uint64_t **mods, uint64_t *size, uint64_t *capacity){ int pipe_xor_bits = ilog2(adev->gfx.config.gb_addr_config_fields.num_pipes);
amdgpu_dm_plane_add_modifier(mods, size, capacity, AMD_FMT_MOD | AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX9_64K_R_X) | AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX10) | AMD_FMT_MOD_SET(PIPE_XOR_BITS, pipe_xor_bits) | AMD_FMT_MOD_SET(DCC, 1) | AMD_FMT_MOD_SET(DCC_CONSTANT_ENCODE, 1) | AMD_FMT_MOD_SET(DCC_INDEPENDENT_64B, 1) | AMD_FMT_MOD_SET(DCC_MAX_COMPRESSED_BLOCK, AMD_FMT_MOD_DCC_BLOCK_64B));
amdgpu_dm_plane_add_modifier(mods, size, capacity, AMD_FMT_MOD | AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX9_64K_R_X) | AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX10) | AMD_FMT_MOD_SET(PIPE_XOR_BITS, pipe_xor_bits) | AMD_FMT_MOD_SET(DCC, 1) | AMD_FMT_MOD_SET(DCC_RETILE, 1) | AMD_FMT_MOD_SET(DCC_CONSTANT_ENCODE, 1) | AMD_FMT_MOD_SET(DCC_INDEPENDENT_64B, 1) | AMD_FMT_MOD_SET(DCC_MAX_COMPRESSED_BLOCK, AMD_FMT_MOD_DCC_BLOCK_64B));
amdgpu_dm_plane_add_modifier(mods, size, capacity, AMD_FMT_MOD | AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX9_64K_R_X) | AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX10) | AMD_FMT_MOD_SET(PIPE_XOR_BITS, pipe_xor_bits));
amdgpu_dm_plane_add_modifier(mods, size, capacity, AMD_FMT_MOD | AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX9_64K_S_X) | AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX10) | AMD_FMT_MOD_SET(PIPE_XOR_BITS, pipe_xor_bits));
/* Only supported for 64bpp, will be filtered in amdgpu_dm_plane_format_mod_supported */ amdgpu_dm_plane_add_modifier(mods, size, capacity, AMD_FMT_MOD | AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX9_64K_D) | AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX9));
amdgpu_dm_plane_add_modifier(mods, size, capacity, AMD_FMT_MOD | AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX9_64K_S) | AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX9));}
/** * amdgpu_dm_plane_get_gfx6_tile_idx() - Get tile mode index on GFX6-8 * * @adev: amdgpu_device pointer * @bpp: bits per pixel in the image * @arr: array mode (aka. tile mode) of the image layout * * Select which tiling mode from the table is suitable for the given bits per pixel * using the given array mode, assuming the displayable micro tile mode is used. * * On GFX6-8, the GFX block can only use pre-programmed tiling modes from GB_TILE_MODEn * registers which are programmed by the kernel according to the tiling mode table. * Note that the tiling table is uAPI and userspace relies on specific modes being * present at specific indices. * See SiLib::HwlSetupTileInfo() and CiLib::HwlSetupTileInfo() in addrlib. */STATIC_IFN_KUNIT u32 amdgpu_dm_plane_get_gfx6_tile_idx(const struct amdgpu_device *adev, const u32 bpp, const enum array_mode_values arr){ /* Assume that the microtile mode is DISPLAY. */
if (arr == DC_ARRAY_1D_TILED_THIN1) return 9;
ASSERT(arr == DC_ARRAY_2D_TILED_THIN1);
if (adev->family > AMDGPU_FAMILY_SI) return 10;
switch (bpp) { case 8: return 10; case 16: return 11; default: return 12; }}EXPORT_IF_KUNIT(amdgpu_dm_plane_get_gfx6_tile_idx);
/** * amdgpu_dm_plane_calc_gfx7_tile_split() - Calculate tile split on GFX7-8 * * @adev: amdgpu_device pointer * @bpp: bits per pixel in the image * @gb_tile_mode: GB_TILE_MODEn register value for the current tiling mode * * Calculate the actual tile split value on GFX7-8, assuming 2D_TILED_THIN1 array mode * with a non-depth micro tile mode. * * On GFX7-8, SAMPLE_SPLIT holds a factor from which the actual tile split bytes * can be calculated. The TILE_SPLIT field is only used for the depth micro tile mode. * See CiLib::HwlComputeMacroModeIndex() in addrlib. */STATIC_IFN_KUNIT u32 amdgpu_dm_plane_calc_gfx7_tile_split(const struct amdgpu_device *adev, const u32 bpp, const u32 gb_tile_mode){ /* Assume 2D_TILED_THIN1 mode with non-DEPTH microtiles */ const u32 sample_split = (gb_tile_mode >> 25) & 0x3; const u32 thickness = 1; const u32 tile_size_pixels = 8 * 8; const u32 tile_bytes_1x = tile_size_pixels * bpp * thickness / 8; const u32 sample_split_factor = 1 << sample_split;
return clamp(tile_bytes_1x * sample_split_factor, 256, adev->gfx.config.mem_row_size_in_kb * 1024);}EXPORT_IF_KUNIT(amdgpu_dm_plane_calc_gfx7_tile_split);
/** * amdgpu_dm_plane_get_gfx7_macro_tile_idx() - Get macro tile mode index on GFX7-8 * * @bpp: bits per pixel in the image * @tile_split_bytes: actual tile split bytes, see amdgpu_dm_plane_calc_gfx7_tile_split() * * Select which macro tiling mode from the table is suitable for the given bits per pixel, * assuming 2D_TILED_THIN1 array mode and DISPLAY micro tile mode (and no multisampling). * Note that the tiling table is uAPI and userspace relies on specific modes being * present at specific indices. * See CiLib::HwlComputeMacroModeIndex() in addrlib. */STATIC_IFN_KUNIT u32 amdgpu_dm_plane_get_gfx7_macro_tile_idx(const u32 bpp, const u32 tile_split_bytes){ const u32 thickness = 1; const u32 tile_size_pixels = 8 * 8; const u32 tile_bytes_1x = thickness * tile_size_pixels * bpp / 8; const u32 tile_bytes = clamp(tile_bytes_1x, 64, tile_split_bytes); const u32 macro_tile_idx = ilog2(tile_bytes / 64);
WARN_ON(macro_tile_idx >= 16);
return macro_tile_idx;}EXPORT_IF_KUNIT(amdgpu_dm_plane_get_gfx7_macro_tile_idx);
/** * amdgpu_dm_plane_calc_gfx6_mod() - Calculate a DRM format modifier for GFX6-8 * * @adev: amdgpu_device pointer * @bpp: bits per pixel in the image * @arr: array mode (aka. tile mode) of the image layout * * Select suitable micro and macro tile modes for the given bits per pixel, * and calculate the corresponding DRM format modifier. */STATIC_IFN_KUNIT u64 amdgpu_dm_plane_calc_gfx6_mod(const struct amdgpu_device *adev, const u32 bpp, const enum array_mode_values arr){ u32 array_mode, micro_tile_mode, tile_split_bytes; u32 gb_macrotile_mode, macrotile_idx; u32 gb_tile_mode, tile_idx;
u64 modifier_base = AMD_FMT_MOD | AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX6) | AMD_FMT_MOD_SET(TILE, arr) | AMD_FMT_MOD_SET(MICROTILE, AMD_FMT_MOD_MICROTILE_DISPLAY);
if (arr < DC_ARRAY_2D_TILED_THIN1) return modifier_base;
tile_idx = amdgpu_dm_plane_get_gfx6_tile_idx(adev, bpp, arr); gb_tile_mode = adev->gfx.config.tile_mode_array[tile_idx]; array_mode = (gb_tile_mode >> 2) & 0xf;
if (adev->family == AMDGPU_FAMILY_SI) { micro_tile_mode = (gb_tile_mode >> 0) & 0x3; tile_split_bytes = 64 << ((gb_tile_mode >> 11) & 0x7); } else { micro_tile_mode = (gb_tile_mode >> 22) & 0x7; tile_split_bytes = amdgpu_dm_plane_calc_gfx7_tile_split(adev, bpp, gb_tile_mode); }
ASSERT(array_mode == arr); ASSERT(micro_tile_mode == AMD_FMT_MOD_MICROTILE_DISPLAY);
modifier_base |= AMD_FMT_MOD_SET(PIPE_CONFIG, (gb_tile_mode >> 6) & 0x1f) | AMD_FMT_MOD_SET(TILE_SPLIT, ilog2(tile_split_bytes / 64));
if (adev->family == AMDGPU_FAMILY_SI) return modifier_base | AMD_FMT_MOD_SET(BANK_WIDTH, (gb_tile_mode >> 14) & 0x3) | AMD_FMT_MOD_SET(BANK_HEIGHT, (gb_tile_mode >> 16) & 0x3) | AMD_FMT_MOD_SET(MACRO_TILE_ASPECT, (gb_tile_mode >> 18) & 0x3) | AMD_FMT_MOD_SET(NUM_BANKS, (gb_tile_mode >> 20) & 0x3);
macrotile_idx = amdgpu_dm_plane_get_gfx7_macro_tile_idx(bpp, tile_split_bytes); gb_macrotile_mode = adev->gfx.config.macrotile_mode_array[macrotile_idx];
return modifier_base | AMD_FMT_MOD_SET(BANK_WIDTH, (gb_macrotile_mode >> 0) & 0x3) | AMD_FMT_MOD_SET(BANK_HEIGHT, (gb_macrotile_mode >> 2) & 0x3) | AMD_FMT_MOD_SET(MACRO_TILE_ASPECT, (gb_macrotile_mode >> 4) & 0x3) | AMD_FMT_MOD_SET(NUM_BANKS, (gb_macrotile_mode >> 6) & 0x3);}EXPORT_IF_KUNIT(amdgpu_dm_plane_calc_gfx6_mod);
/** * amdgpu_dm_plane_gfx6_format_mod_supported() - Check if a modifier is supported on GFX6-8 * * @adev: amdgpu_device pointer * @bpp: bits per pixel in the image * @modifier: the modifier whose support we check * * On GFX6-8, not all DRM format modifier can be used with all image formats. * Check whether the specified modifier is supported with the given bits per pixel value. */STATIC_IFN_KUNIT bool amdgpu_dm_plane_gfx6_format_mod_supported(const struct amdgpu_device *adev, const u32 bpp, const u64 modifier){ const u32 array_mode = AMD_FMT_MOD_GET(TILE, modifier); const u32 micro_tile_mode = AMD_FMT_MOD_GET(MICROTILE, modifier);
if (!IS_AMD_FMT_MOD(modifier)) return false;
/* GFX9 and newer format modifiers are not supported on GFX6-8. */ if (AMD_FMT_MOD_GET(TILE_VERSION, modifier) != AMD_FMT_MOD_TILE_VER_GFX6) return false;
/* GFX8 and older format modifiers are not supported on GFX9+. */ if (adev->family >= AMDGPU_FAMILY_AI) return false;
/* GFX6-7 doesn't have any DCC, GFX8 doesn't support displayable DCC. */ if (AMD_FMT_MOD_GET(DCC, modifier)) return false;
/* * For now, only expose 1D and 2D tiles THIN array modes. * Linear is already exposed through DRM_FORMAT_MOD_LINEAR. */ if (array_mode != AMD_FMT_MOD_TILE_GFX6_1D_TILED_THIN1 && array_mode != AMD_FMT_MOD_TILE_GFX6_2D_TILED_THIN1) return false;
/* * For now, only allow DISPLAY micro tile mode. * THIN, DEPTH and THICK modes are not displayable. * ROTATED has never been supported by Linux or Mesa. */ if (micro_tile_mode != AMD_FMT_MOD_MICROTILE_DISPLAY) return false;
/* Verify that the modifier is the same that we'd expose for this bpp */ return amdgpu_dm_plane_calc_gfx6_mod(adev, bpp, array_mode) == modifier;}EXPORT_IF_KUNIT(amdgpu_dm_plane_gfx6_format_mod_supported);
/** * amdgpu_dm_plane_add_gfx6_modifiers() - Expose modifiers for GFX6-8 * * @adev: amdgpu_device pointer * @mods: Pointer to array of format modifiers * @size: Pointer to size of the array * @capacity: Pointer to capacity of the array * * Calculate a DRM format modifier for macro tiled modes for each supported * bits per pixel value. Use de-duplication because on some GPUs it may happen * that different bpp results in the exact same macro tiling mode, depending * on the tiling table. * * Also expose a micro tiled only mode. This is less optimal, but supported. */static void amdgpu_dm_plane_add_gfx6_modifiers(const struct amdgpu_device *adev, u64 **mods, u64 *size, u64 *capacity){ /* 2D tiled displayable */ amdgpu_dm_plane_add_modifier(mods, size, capacity, amdgpu_dm_plane_calc_gfx6_mod(adev, 16, DC_ARRAY_2D_TILED_THIN1)); amdgpu_dm_plane_add_modifier_dedup(mods, size, capacity, amdgpu_dm_plane_calc_gfx6_mod(adev, 32, DC_ARRAY_2D_TILED_THIN1)); amdgpu_dm_plane_add_modifier_dedup(mods, size, capacity, amdgpu_dm_plane_calc_gfx6_mod(adev, 64, DC_ARRAY_2D_TILED_THIN1));
/* 1D tiled displayable */ amdgpu_dm_plane_add_modifier(mods, size, capacity, amdgpu_dm_plane_calc_gfx6_mod(adev, 0, DC_ARRAY_1D_TILED_THIN1));}
static void amdgpu_dm_plane_add_gfx9_modifiers(const struct amdgpu_device *adev, uint64_t **mods, uint64_t *size, uint64_t *capacity){ int pipes = ilog2(adev->gfx.config.gb_addr_config_fields.num_pipes); int pipe_xor_bits = min(8, pipes + ilog2(adev->gfx.config.gb_addr_config_fields.num_se)); int bank_xor_bits = min(8 - pipe_xor_bits, ilog2(adev->gfx.config.gb_addr_config_fields.num_banks)); int rb = ilog2(adev->gfx.config.gb_addr_config_fields.num_se) + ilog2(adev->gfx.config.gb_addr_config_fields.num_rb_per_se);
if (adev->family == AMDGPU_FAMILY_RV) { /* Raven2 and later */ bool has_constant_encode = adev->asic_type > CHIP_RAVEN || adev->external_rev_id >= 0x81;
/* * No _D DCC swizzles yet because we only allow 32bpp, which * doesn't support _D on DCN */
if (has_constant_encode) { amdgpu_dm_plane_add_modifier(mods, size, capacity, AMD_FMT_MOD | AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX9_64K_S_X) | AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX9) | AMD_FMT_MOD_SET(PIPE_XOR_BITS, pipe_xor_bits) | AMD_FMT_MOD_SET(BANK_XOR_BITS, bank_xor_bits) | AMD_FMT_MOD_SET(DCC, 1) | AMD_FMT_MOD_SET(DCC_INDEPENDENT_64B, 1) | AMD_FMT_MOD_SET(DCC_MAX_COMPRESSED_BLOCK, AMD_FMT_MOD_DCC_BLOCK_64B) | AMD_FMT_MOD_SET(DCC_CONSTANT_ENCODE, 1)); }
amdgpu_dm_plane_add_modifier(mods, size, capacity, AMD_FMT_MOD | AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX9_64K_S_X) | AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX9) | AMD_FMT_MOD_SET(PIPE_XOR_BITS, pipe_xor_bits) | AMD_FMT_MOD_SET(BANK_XOR_BITS, bank_xor_bits) | AMD_FMT_MOD_SET(DCC, 1) | AMD_FMT_MOD_SET(DCC_INDEPENDENT_64B, 1) | AMD_FMT_MOD_SET(DCC_MAX_COMPRESSED_BLOCK, AMD_FMT_MOD_DCC_BLOCK_64B) | AMD_FMT_MOD_SET(DCC_CONSTANT_ENCODE, 0));
if (has_constant_encode) { amdgpu_dm_plane_add_modifier(mods, size, capacity, AMD_FMT_MOD | AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX9_64K_S_X) | AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX9) | AMD_FMT_MOD_SET(PIPE_XOR_BITS, pipe_xor_bits) | AMD_FMT_MOD_SET(BANK_XOR_BITS, bank_xor_bits) | AMD_FMT_MOD_SET(DCC, 1) | AMD_FMT_MOD_SET(DCC_RETILE, 1) | AMD_FMT_MOD_SET(DCC_INDEPENDENT_64B, 1) | AMD_FMT_MOD_SET(DCC_MAX_COMPRESSED_BLOCK, AMD_FMT_MOD_DCC_BLOCK_64B) | AMD_FMT_MOD_SET(DCC_CONSTANT_ENCODE, 1) | AMD_FMT_MOD_SET(RB, rb) | AMD_FMT_MOD_SET(PIPE, pipes)); }
amdgpu_dm_plane_add_modifier(mods, size, capacity, AMD_FMT_MOD | AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX9_64K_S_X) | AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX9) | AMD_FMT_MOD_SET(PIPE_XOR_BITS, pipe_xor_bits) | AMD_FMT_MOD_SET(BANK_XOR_BITS, bank_xor_bits) | AMD_FMT_MOD_SET(DCC, 1) | AMD_FMT_MOD_SET(DCC_RETILE, 1) | AMD_FMT_MOD_SET(DCC_INDEPENDENT_64B, 1) | AMD_FMT_MOD_SET(DCC_MAX_COMPRESSED_BLOCK, AMD_FMT_MOD_DCC_BLOCK_64B) | AMD_FMT_MOD_SET(DCC_CONSTANT_ENCODE, 0) | AMD_FMT_MOD_SET(RB, rb) | AMD_FMT_MOD_SET(PIPE, pipes)); }
/* * Only supported for 64bpp on Raven, will be filtered on format in * amdgpu_dm_plane_format_mod_supported. */ amdgpu_dm_plane_add_modifier(mods, size, capacity, AMD_FMT_MOD | AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX9_64K_D_X) | AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX9) | AMD_FMT_MOD_SET(PIPE_XOR_BITS, pipe_xor_bits) | AMD_FMT_MOD_SET(BANK_XOR_BITS, bank_xor_bits));
if (adev->family == AMDGPU_FAMILY_RV) { amdgpu_dm_plane_add_modifier(mods, size, capacity, AMD_FMT_MOD | AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX9_64K_S_X) | AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX9) | AMD_FMT_MOD_SET(PIPE_XOR_BITS, pipe_xor_bits) | AMD_FMT_MOD_SET(BANK_XOR_BITS, bank_xor_bits)); }
/* * Only supported for 64bpp on Raven, will be filtered on format in * amdgpu_dm_plane_format_mod_supported. */ amdgpu_dm_plane_add_modifier(mods, size, capacity, AMD_FMT_MOD | AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX9_64K_D) | AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX9));
if (adev->family == AMDGPU_FAMILY_RV) { amdgpu_dm_plane_add_modifier(mods, size, capacity, AMD_FMT_MOD | AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX9_64K_S) | AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX9)); }}
static void amdgpu_dm_plane_add_gfx10_3_modifiers(const struct amdgpu_device *adev, uint64_t **mods, uint64_t *size, uint64_t *capacity){ int pipe_xor_bits = ilog2(adev->gfx.config.gb_addr_config_fields.num_pipes); int pkrs = ilog2(adev->gfx.config.gb_addr_config_fields.num_pkrs);
amdgpu_dm_plane_add_modifier(mods, size, capacity, AMD_FMT_MOD | AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX9_64K_R_X) | AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX10_RBPLUS) | AMD_FMT_MOD_SET(PIPE_XOR_BITS, pipe_xor_bits) | AMD_FMT_MOD_SET(PACKERS, pkrs) | AMD_FMT_MOD_SET(DCC, 1) | AMD_FMT_MOD_SET(DCC_CONSTANT_ENCODE, 1) | AMD_FMT_MOD_SET(DCC_INDEPENDENT_64B, 1) | AMD_FMT_MOD_SET(DCC_INDEPENDENT_128B, 1) | AMD_FMT_MOD_SET(DCC_MAX_COMPRESSED_BLOCK, AMD_FMT_MOD_DCC_BLOCK_64B));
amdgpu_dm_plane_add_modifier(mods, size, capacity, AMD_FMT_MOD | AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX9_64K_R_X) | AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX10_RBPLUS) | AMD_FMT_MOD_SET(PIPE_XOR_BITS, pipe_xor_bits) | AMD_FMT_MOD_SET(PACKERS, pkrs) | AMD_FMT_MOD_SET(DCC, 1) | AMD_FMT_MOD_SET(DCC_CONSTANT_ENCODE, 1) | AMD_FMT_MOD_SET(DCC_INDEPENDENT_128B, 1) | AMD_FMT_MOD_SET(DCC_MAX_COMPRESSED_BLOCK, AMD_FMT_MOD_DCC_BLOCK_128B));
amdgpu_dm_plane_add_modifier(mods, size, capacity, AMD_FMT_MOD | AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX9_64K_R_X) | AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX10_RBPLUS) | AMD_FMT_MOD_SET(PIPE_XOR_BITS, pipe_xor_bits) | AMD_FMT_MOD_SET(PACKERS, pkrs) | AMD_FMT_MOD_SET(DCC, 1) | AMD_FMT_MOD_SET(DCC_RETILE, 1) | AMD_FMT_MOD_SET(DCC_CONSTANT_ENCODE, 1) | AMD_FMT_MOD_SET(DCC_INDEPENDENT_64B, 1) | AMD_FMT_MOD_SET(DCC_INDEPENDENT_128B, 1) | AMD_FMT_MOD_SET(DCC_MAX_COMPRESSED_BLOCK, AMD_FMT_MOD_DCC_BLOCK_64B));
amdgpu_dm_plane_add_modifier(mods, size, capacity, AMD_FMT_MOD | AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX9_64K_R_X) | AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX10_RBPLUS) | AMD_FMT_MOD_SET(PIPE_XOR_BITS, pipe_xor_bits) | AMD_FMT_MOD_SET(PACKERS, pkrs) | AMD_FMT_MOD_SET(DCC, 1) | AMD_FMT_MOD_SET(DCC_RETILE, 1) | AMD_FMT_MOD_SET(DCC_CONSTANT_ENCODE, 1) | AMD_FMT_MOD_SET(DCC_INDEPENDENT_128B, 1) | AMD_FMT_MOD_SET(DCC_MAX_COMPRESSED_BLOCK, AMD_FMT_MOD_DCC_BLOCK_128B));
amdgpu_dm_plane_add_modifier(mods, size, capacity, AMD_FMT_MOD | AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX9_64K_R_X) | AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX10_RBPLUS) | AMD_FMT_MOD_SET(PIPE_XOR_BITS, pipe_xor_bits) | AMD_FMT_MOD_SET(PACKERS, pkrs));
amdgpu_dm_plane_add_modifier(mods, size, capacity, AMD_FMT_MOD | AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX9_64K_S_X) | AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX10_RBPLUS) | AMD_FMT_MOD_SET(PIPE_XOR_BITS, pipe_xor_bits) | AMD_FMT_MOD_SET(PACKERS, pkrs));
/* Only supported for 64bpp, will be filtered in amdgpu_dm_plane_format_mod_supported */ amdgpu_dm_plane_add_modifier(mods, size, capacity, AMD_FMT_MOD | AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX9_64K_D) | AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX9));
amdgpu_dm_plane_add_modifier(mods, size, capacity, AMD_FMT_MOD | AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX9_64K_S) | AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX9));}
static void amdgpu_dm_plane_add_gfx11_modifiers(struct amdgpu_device *adev, uint64_t **mods, uint64_t *size, uint64_t *capacity){ int num_pipes = 0; int pipe_xor_bits = 0; int num_pkrs = 0; int pkrs = 0; u32 gb_addr_config; u8 i = 0; unsigned int swizzle_r_x; uint64_t modifier_r_x; uint64_t modifier_dcc_best; uint64_t modifier_dcc_4k;
/* TODO: GFX11 IP HW init hasnt finish and we get zero if we read from * adev->gfx.config.gb_addr_config_fields.num_{pkrs,pipes} */ gb_addr_config = RREG32_SOC15(GC, 0, regGB_ADDR_CONFIG); ASSERT(gb_addr_config != 0);
num_pkrs = 1 << REG_GET_FIELD(gb_addr_config, GB_ADDR_CONFIG, NUM_PKRS); pkrs = ilog2(num_pkrs); num_pipes = 1 << REG_GET_FIELD(gb_addr_config, GB_ADDR_CONFIG, NUM_PIPES); pipe_xor_bits = ilog2(num_pipes);
for (i = 0; i < 2; i++) { /* Insert the best one first. */ /* R_X swizzle modes are the best for rendering and DCC requires them. */ if (num_pipes > 16) swizzle_r_x = !i ? AMD_FMT_MOD_TILE_GFX11_256K_R_X : AMD_FMT_MOD_TILE_GFX9_64K_R_X; else swizzle_r_x = !i ? AMD_FMT_MOD_TILE_GFX9_64K_R_X : AMD_FMT_MOD_TILE_GFX11_256K_R_X;
modifier_r_x = AMD_FMT_MOD | AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX11) | AMD_FMT_MOD_SET(PIPE_XOR_BITS, pipe_xor_bits) | AMD_FMT_MOD_SET(TILE, swizzle_r_x) | AMD_FMT_MOD_SET(PACKERS, pkrs);
/* DCC_CONSTANT_ENCODE is not set because it can't vary with gfx11 (it's implied to be 1). */ modifier_dcc_best = modifier_r_x | AMD_FMT_MOD_SET(DCC, 1) | AMD_FMT_MOD_SET(DCC_INDEPENDENT_64B, 0) | AMD_FMT_MOD_SET(DCC_INDEPENDENT_128B, 1) | AMD_FMT_MOD_SET(DCC_MAX_COMPRESSED_BLOCK, AMD_FMT_MOD_DCC_BLOCK_128B);
/* DCC settings for 4K and greater resolutions. (required by display hw) */ modifier_dcc_4k = modifier_r_x | AMD_FMT_MOD_SET(DCC, 1) | AMD_FMT_MOD_SET(DCC_INDEPENDENT_64B, 1) | AMD_FMT_MOD_SET(DCC_INDEPENDENT_128B, 1) | AMD_FMT_MOD_SET(DCC_MAX_COMPRESSED_BLOCK, AMD_FMT_MOD_DCC_BLOCK_64B);
amdgpu_dm_plane_add_modifier(mods, size, capacity, modifier_dcc_best); amdgpu_dm_plane_add_modifier(mods, size, capacity, modifier_dcc_4k);
amdgpu_dm_plane_add_modifier(mods, size, capacity, modifier_dcc_best | AMD_FMT_MOD_SET(DCC_RETILE, 1)); amdgpu_dm_plane_add_modifier(mods, size, capacity, modifier_dcc_4k | AMD_FMT_MOD_SET(DCC_RETILE, 1));
amdgpu_dm_plane_add_modifier(mods, size, capacity, modifier_r_x); }
amdgpu_dm_plane_add_modifier(mods, size, capacity, AMD_FMT_MOD | AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX11) | AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX9_64K_D));}
static void amdgpu_dm_plane_add_gfx12_modifiers(struct amdgpu_device *adev, uint64_t **mods, uint64_t *size, uint64_t *capacity){ uint64_t ver = AMD_FMT_MOD | AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX12); uint64_t mod_256k = ver | AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX12_256K_2D); uint64_t mod_64k = ver | AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX12_64K_2D); uint64_t mod_4k = ver | AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX12_4K_2D); uint64_t mod_256b = ver | AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX12_256B_2D); uint64_t dcc = ver | AMD_FMT_MOD_SET(DCC, 1); uint8_t max_comp_block[] = {2, 1, 0}; uint64_t max_comp_block_mod[ARRAY_SIZE(max_comp_block)] = {0}; uint8_t i = 0, j = 0; /* Note, linear (no DCC) gets added to the modifier list for all chips by the caller. */ uint64_t gfx12_modifiers[] = {mod_256k, mod_64k, mod_4k, mod_256b};
for (i = 0; i < ARRAY_SIZE(max_comp_block); i++) max_comp_block_mod[i] = AMD_FMT_MOD_SET(DCC_MAX_COMPRESSED_BLOCK, max_comp_block[i]);
/* With DCC: Best choice should be kept first. Hence, add all 256k modifiers of different * max compressed blocks first and then move on to the next smaller sized layouts. */ for (j = 0; j < ARRAY_SIZE(gfx12_modifiers); j++) for (i = 0; i < ARRAY_SIZE(max_comp_block); i++) amdgpu_dm_plane_add_modifier(mods, size, capacity, ver | dcc | max_comp_block_mod[i] | gfx12_modifiers[j]);
/* Without DCC. */ for (i = 0; i < ARRAY_SIZE(gfx12_modifiers); i++) amdgpu_dm_plane_add_modifier(mods, size, capacity, gfx12_modifiers[i]);
}
STATIC_IFN_KUNIT int amdgpu_dm_plane_get_plane_modifiers(struct amdgpu_device *adev, unsigned int plane_type, uint64_t **mods){ uint64_t size = 0, capacity = 128; *mods = kmalloc_array(capacity, sizeof(uint64_t), GFP_KERNEL);
if (plane_type == DRM_PLANE_TYPE_CURSOR) { amdgpu_dm_plane_add_modifier(mods, &size, &capacity, DRM_FORMAT_MOD_LINEAR); amdgpu_dm_plane_add_modifier(mods, &size, &capacity, DRM_FORMAT_MOD_INVALID); return *mods ? 0 : -ENOMEM; }
switch (adev->family) { case AMDGPU_FAMILY_SI: case AMDGPU_FAMILY_CI: case AMDGPU_FAMILY_KV: case AMDGPU_FAMILY_VI: case AMDGPU_FAMILY_CZ: amdgpu_dm_plane_add_gfx6_modifiers(adev, mods, &size, &capacity); break; case AMDGPU_FAMILY_AI: case AMDGPU_FAMILY_RV: amdgpu_dm_plane_add_gfx9_modifiers(adev, mods, &size, &capacity); break; case AMDGPU_FAMILY_NV: case AMDGPU_FAMILY_VGH: case AMDGPU_FAMILY_YC: case AMDGPU_FAMILY_GC_10_3_6: case AMDGPU_FAMILY_GC_10_3_7: if (amdgpu_ip_version(adev, GC_HWIP, 0) >= IP_VERSION(10, 3, 0)) amdgpu_dm_plane_add_gfx10_3_modifiers(adev, mods, &size, &capacity); else amdgpu_dm_plane_add_gfx10_1_modifiers(adev, mods, &size, &capacity); break; case AMDGPU_FAMILY_GC_11_0_0: case AMDGPU_FAMILY_GC_11_0_1: case AMDGPU_FAMILY_GC_11_5_0: case AMDGPU_FAMILY_GC_11_5_4: amdgpu_dm_plane_add_gfx11_modifiers(adev, mods, &size, &capacity); break; case AMDGPU_FAMILY_GC_12_0_0: case AMDGPU_FAMILY_GC_13_0_1: /* GFX13 (DCN6) shares the GFX12 tiling/DCC binary modifier format. */ amdgpu_dm_plane_add_gfx12_modifiers(adev, mods, &size, &capacity); break; }
amdgpu_dm_plane_add_modifier(mods, &size, &capacity, DRM_FORMAT_MOD_LINEAR);
/* INVALID marks the end of the list. */ amdgpu_dm_plane_add_modifier(mods, &size, &capacity, DRM_FORMAT_MOD_INVALID);
if (!*mods) return -ENOMEM;
return 0;}EXPORT_IF_KUNIT(amdgpu_dm_plane_get_plane_modifiers);
STATIC_IFN_KUNIT int amdgpu_dm_plane_get_plane_formats(const struct drm_plane *plane, const struct dc_plane_cap *plane_cap, uint32_t *formats, int max_formats){ int i, num_formats = 0;
/* * TODO: Query support for each group of formats directly from * DC plane caps. This will require adding more formats to the * caps list. */
if (plane->type == DRM_PLANE_TYPE_PRIMARY || (plane_cap && plane_cap->type == DC_PLANE_TYPE_DCN_UNIVERSAL && plane->type != DRM_PLANE_TYPE_CURSOR)) { for (i = 0; i < ARRAY_SIZE(rgb_formats); ++i) { if (num_formats >= max_formats) break;
formats[num_formats++] = rgb_formats[i]; }
if (plane_cap && plane_cap->pixel_format_support.nv12) formats[num_formats++] = DRM_FORMAT_NV12; if (plane_cap && plane_cap->pixel_format_support.p010) formats[num_formats++] = DRM_FORMAT_P010; if (plane_cap && plane_cap->pixel_format_support.fp16) { formats[num_formats++] = DRM_FORMAT_XRGB16161616F; formats[num_formats++] = DRM_FORMAT_ARGB16161616F; formats[num_formats++] = DRM_FORMAT_XBGR16161616F; formats[num_formats++] = DRM_FORMAT_ABGR16161616F; } } else { switch (plane->type) { case DRM_PLANE_TYPE_OVERLAY: for (i = 0; i < ARRAY_SIZE(overlay_formats); ++i) { if (num_formats >= max_formats) break;
formats[num_formats++] = overlay_formats[i]; } break;
case DRM_PLANE_TYPE_CURSOR: for (i = 0; i < ARRAY_SIZE(cursor_formats); ++i) { if (num_formats >= max_formats) break;
formats[num_formats++] = cursor_formats[i]; } break;
default: break; } }
return num_formats;}EXPORT_IF_KUNIT(amdgpu_dm_plane_get_plane_formats);
int amdgpu_dm_plane_fill_plane_buffer_attributes(struct amdgpu_device *adev, const struct amdgpu_framebuffer *afb, const enum surface_pixel_format format, const enum dc_rotation_angle rotation, struct dc_tiling_info *tiling_info, struct plane_size *plane_size, struct dc_plane_dcc_param *dcc, struct dc_plane_address *address, bool tmz_surface){ const struct drm_framebuffer *fb = &afb->base; int ret;
memset(tiling_info, 0, sizeof(*tiling_info)); memset(plane_size, 0, sizeof(*plane_size)); memset(dcc, 0, sizeof(*dcc)); memset(address, 0, sizeof(*address));
address->tmz_surface = tmz_surface;
if (format < SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) { uint64_t addr = afb->address + fb->offsets[0];
plane_size->surface_size.x = 0; plane_size->surface_size.y = 0; plane_size->surface_size.width = fb->width; plane_size->surface_size.height = fb->height; plane_size->surface_pitch = fb->pitches[0] / fb->format->cpp[0];
address->type = PLN_ADDR_TYPE_GRAPHICS; address->grph.addr.low_part = lower_32_bits(addr); address->grph.addr.high_part = upper_32_bits(addr); } else if (format < SURFACE_PIXEL_FORMAT_INVALID) { uint64_t luma_addr = afb->address + fb->offsets[0]; uint64_t chroma_addr = afb->address + fb->offsets[1];
plane_size->surface_size.x = 0; plane_size->surface_size.y = 0; plane_size->surface_size.width = fb->width; plane_size->surface_size.height = fb->height; plane_size->surface_pitch = fb->pitches[0] / fb->format->cpp[0];
plane_size->chroma_size.x = 0; plane_size->chroma_size.y = 0; /* TODO: set these based on surface format */ plane_size->chroma_size.width = fb->width / 2; plane_size->chroma_size.height = fb->height / 2;
plane_size->chroma_pitch = fb->pitches[1] / fb->format->cpp[1];
address->type = PLN_ADDR_TYPE_VIDEO_PROGRESSIVE; address->video_progressive.luma_addr.low_part = lower_32_bits(luma_addr); address->video_progressive.luma_addr.high_part = upper_32_bits(luma_addr); address->video_progressive.chroma_addr.low_part = lower_32_bits(chroma_addr); address->video_progressive.chroma_addr.high_part = upper_32_bits(chroma_addr); }
if (adev->family == AMDGPU_FAMILY_GC_12_0_0 || adev->family == AMDGPU_FAMILY_GC_13_0_1) { ret = amdgpu_dm_plane_fill_gfx12_attrs_from_modifiers(adev, afb, format, rotation, plane_size, tiling_info, dcc, address); if (ret) return ret; } else if (adev->family >= AMDGPU_FAMILY_AI) { ret = amdgpu_dm_plane_fill_gfx9_attrs_from_modifiers(adev, afb, format, rotation, plane_size, tiling_info, dcc, address); if (ret) return ret; } else { ret = amdgpu_dm_plane_fill_gfx6_tiling_info_from_modifier(tiling_info, afb->base.modifier); if (ret) return ret; }
return 0;}EXPORT_IF_KUNIT(amdgpu_dm_plane_fill_plane_buffer_attributes);
STATIC_IFN_KUNIT int amdgpu_dm_plane_helper_prepare_fb(struct drm_plane *plane, struct drm_plane_state *new_state){ struct amdgpu_framebuffer *afb; struct drm_gem_object *obj; struct amdgpu_device *adev; struct amdgpu_bo *rbo; struct dm_plane_state *dm_plane_state_new, *dm_plane_state_old; uint32_t domain; int r;
if (!new_state->fb) { DRM_DEBUG_KMS("No FB bound\n"); return 0; }
afb = to_amdgpu_framebuffer(new_state->fb); obj = drm_gem_fb_get_obj(new_state->fb, 0); if (!obj) { DRM_ERROR("Failed to get obj from framebuffer\n"); return -EINVAL; }
rbo = gem_to_amdgpu_bo(obj); adev = amdgpu_ttm_adev(rbo->tbo.bdev); r = amdgpu_bo_reserve(rbo, true); if (r) { drm_err(adev_to_drm(adev), "fail to reserve bo: %pe\n", ERR_PTR(r)); return r; }
r = dma_resv_reserve_fences(rbo->tbo.base.resv, TTM_NUM_MOVE_FENCES); if (r) { drm_err(adev_to_drm(adev), "reserving fence slot failed: %pe\n", ERR_PTR(r)); goto error_unlock; }
if (plane->type != DRM_PLANE_TYPE_CURSOR) domain = amdgpu_display_supported_domains(adev, rbo->flags); else domain = AMDGPU_GEM_DOMAIN_VRAM;
rbo->flags |= AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS; r = amdgpu_bo_pin(rbo, domain); if (unlikely(r != 0)) { if (r != -ERESTARTSYS) DRM_ERROR("Failed to pin framebuffer: %pe\n", ERR_PTR(r)); goto error_unlock; }
r = amdgpu_ttm_alloc_gart(&rbo->tbo); if (unlikely(r != 0)) { DRM_ERROR("%p bind failed: %pe\n", rbo, ERR_PTR(r)); goto error_unpin; }
r = drm_gem_plane_helper_prepare_fb(plane, new_state); if (unlikely(r != 0)) goto error_unpin;
amdgpu_bo_unreserve(rbo);
afb->address = amdgpu_bo_gpu_offset(rbo);
amdgpu_bo_ref(rbo);
/** * We don't do surface updates on planes that have been newly created, * but we also don't have the afb->address during atomic check. * * Fill in buffer attributes depending on the address here, but only on * newly created planes since they're not being used by DC yet and this * won't modify global state. */ dm_plane_state_old = to_dm_plane_state(plane->state); dm_plane_state_new = to_dm_plane_state(new_state);
if (dm_plane_state_new->dc_state && dm_plane_state_old->dc_state != dm_plane_state_new->dc_state) { struct dc_plane_state *plane_state = dm_plane_state_new->dc_state;
amdgpu_dm_plane_fill_plane_buffer_attributes( adev, afb, plane_state->format, plane_state->rotation, &plane_state->tiling_info, &plane_state->plane_size, &plane_state->dcc, &plane_state->address, afb->tmz_surface); }
return 0;
error_unpin: amdgpu_bo_unpin(rbo);
error_unlock: amdgpu_bo_unreserve(rbo); return r;}EXPORT_IF_KUNIT(amdgpu_dm_plane_helper_prepare_fb);
STATIC_IFN_KUNIT void amdgpu_dm_plane_helper_cleanup_fb(struct drm_plane *plane, struct drm_plane_state *old_state){ struct amdgpu_bo *rbo; int r;
if (!old_state->fb) return;
rbo = gem_to_amdgpu_bo(old_state->fb->obj[0]); r = amdgpu_bo_reserve(rbo, false); if (unlikely(r)) { DRM_ERROR("failed to reserve rbo before unpin: %pe\n", ERR_PTR(r)); return; }
amdgpu_bo_unpin(rbo); amdgpu_bo_unreserve(rbo); amdgpu_bo_unref(&rbo);}EXPORT_IF_KUNIT(amdgpu_dm_plane_helper_cleanup_fb);
STATIC_IFN_KUNIT void amdgpu_dm_plane_get_min_max_dc_plane_scaling(struct drm_device *dev, struct drm_framebuffer *fb, int *min_downscale, int *max_upscale){ struct amdgpu_device *adev = drm_to_adev(dev); struct dc *dc = adev->dm.dc; /* Caps for all supported planes are the same on DCE and DCN 1 - 3 */ struct dc_plane_cap *plane_cap = &dc->caps.planes[0];
switch (fb->format->format) { case DRM_FORMAT_P010: case DRM_FORMAT_NV12: case DRM_FORMAT_NV21: *max_upscale = plane_cap->max_upscale_factor.nv12; *min_downscale = plane_cap->max_downscale_factor.nv12; break;
/* All 64 bpp formats have the same fp16 scaling limits */ case DRM_FORMAT_XRGB16161616F: case DRM_FORMAT_ARGB16161616F: case DRM_FORMAT_XBGR16161616F: case DRM_FORMAT_ABGR16161616F: case DRM_FORMAT_XRGB16161616: case DRM_FORMAT_ARGB16161616: case DRM_FORMAT_XBGR16161616: case DRM_FORMAT_ABGR16161616: *max_upscale = plane_cap->max_upscale_factor.fp16; *min_downscale = plane_cap->max_downscale_factor.fp16; break;
default: *max_upscale = plane_cap->max_upscale_factor.argb8888; *min_downscale = plane_cap->max_downscale_factor.argb8888; break; }
/* * A factor of 1 in the plane_cap means to not allow scaling, ie. use a * scaling factor of 1.0 == 1000 units. */ if (*max_upscale == 1) *max_upscale = 1000;
if (*min_downscale == 1) *min_downscale = 1000;}EXPORT_IF_KUNIT(amdgpu_dm_plane_get_min_max_dc_plane_scaling);
int amdgpu_dm_plane_helper_check_state(struct drm_plane_state *state, struct drm_crtc_state *new_crtc_state){ struct drm_framebuffer *fb = state->fb; int min_downscale, max_upscale; int min_scale = 0; int max_scale = INT_MAX;
/* Plane enabled? Validate viewport and get scaling factors from plane caps. */ if (fb && state->crtc) { /* Validate viewport to cover the case when only the position changes */ if (state->plane->type != DRM_PLANE_TYPE_CURSOR) { int viewport_width = state->crtc_w; int viewport_height = state->crtc_h;
if (state->crtc_x < 0) viewport_width += state->crtc_x; else if (state->crtc_x + state->crtc_w > new_crtc_state->mode.crtc_hdisplay) viewport_width = new_crtc_state->mode.crtc_hdisplay - state->crtc_x;
if (state->crtc_y < 0) viewport_height += state->crtc_y; else if (state->crtc_y + state->crtc_h > new_crtc_state->mode.crtc_vdisplay) viewport_height = new_crtc_state->mode.crtc_vdisplay - state->crtc_y;
if (viewport_width < 0 || viewport_height < 0) { DRM_DEBUG_ATOMIC("Plane completely outside of screen\n"); return -EINVAL; } else if (viewport_width < MIN_VIEWPORT_SIZE*2) { /* x2 for width is because of pipe-split. */ DRM_DEBUG_ATOMIC("Viewport width %d smaller than %d\n", viewport_width, MIN_VIEWPORT_SIZE*2); return -EINVAL; } else if (viewport_height < MIN_VIEWPORT_SIZE) { DRM_DEBUG_ATOMIC("Viewport height %d smaller than %d\n", viewport_height, MIN_VIEWPORT_SIZE); return -EINVAL; }
}
/* Get min/max allowed scaling factors from plane caps. */ amdgpu_dm_plane_get_min_max_dc_plane_scaling(state->crtc->dev, fb, &min_downscale, &max_upscale); /* * Convert to drm convention: 16.16 fixed point, instead of dc's * 1.0 == 1000. Also drm scaling is src/dst instead of dc's * dst/src, so min_scale = 1.0 / max_upscale, etc. */ min_scale = (1000 << 16) / max_upscale; max_scale = (1000 << 16) / min_downscale; }
return drm_atomic_helper_check_plane_state( state, new_crtc_state, min_scale, max_scale, true, true);}EXPORT_IF_KUNIT(amdgpu_dm_plane_helper_check_state);
int amdgpu_dm_plane_fill_dc_scaling_info(struct amdgpu_device *adev, const struct drm_plane_state *state, struct dc_scaling_info *scaling_info){ int scale_w, scale_h, min_downscale, max_upscale;
memset(scaling_info, 0, sizeof(*scaling_info));
/* Source is fixed 16.16 but we ignore mantissa for now... */ scaling_info->src_rect.x = state->src_x >> 16; scaling_info->src_rect.y = state->src_y >> 16;
/* * For reasons we don't (yet) fully understand a non-zero * src_y coordinate into an NV12 buffer can cause a * system hang on DCN1x. * To avoid hangs (and maybe be overly cautious) * let's reject both non-zero src_x and src_y. * * We currently know of only one use-case to reproduce a * scenario with non-zero src_x and src_y for NV12, which * is to gesture the YouTube Android app into full screen * on ChromeOS. */ if (((amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(1, 0, 0)) || (amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(1, 0, 1))) && (state->fb && state->fb->format->format == DRM_FORMAT_NV12 && (scaling_info->src_rect.x != 0 || scaling_info->src_rect.y != 0))) return -EINVAL;
scaling_info->src_rect.width = state->src_w >> 16; if (scaling_info->src_rect.width == 0) return -EINVAL;
scaling_info->src_rect.height = state->src_h >> 16; if (scaling_info->src_rect.height == 0) return -EINVAL;
scaling_info->dst_rect.x = state->crtc_x; scaling_info->dst_rect.y = state->crtc_y;
if (state->crtc_w == 0) return -EINVAL;
scaling_info->dst_rect.width = state->crtc_w;
if (state->crtc_h == 0) return -EINVAL;
scaling_info->dst_rect.height = state->crtc_h;
/* DRM doesn't specify clipping on destination output. */ scaling_info->clip_rect = scaling_info->dst_rect;
/* Validate scaling per-format with DC plane caps */ if (state->plane && state->plane->dev && state->fb) { amdgpu_dm_plane_get_min_max_dc_plane_scaling(state->plane->dev, state->fb, &min_downscale, &max_upscale); } else { min_downscale = 250; max_upscale = 16000; }
scale_w = scaling_info->dst_rect.width * 1000 / scaling_info->src_rect.width;
if (scale_w < min_downscale || scale_w > max_upscale) return -EINVAL;
scale_h = scaling_info->dst_rect.height * 1000 / scaling_info->src_rect.height;
if (scale_h < min_downscale || scale_h > max_upscale) return -EINVAL;
/* * The "scaling_quality" can be ignored for now, quality = 0 has DC * assume reasonable defaults based on the format. */
return 0;}EXPORT_IF_KUNIT(amdgpu_dm_plane_fill_dc_scaling_info);
STATIC_IFN_KUNIT int amdgpu_dm_plane_atomic_check(struct drm_plane *plane, struct drm_atomic_commit *state){ struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); struct amdgpu_device *adev = drm_to_adev(plane->dev); struct dc *dc = adev->dm.dc; struct dm_plane_state *dm_plane_state; struct dc_scaling_info scaling_info; struct drm_crtc_state *new_crtc_state; int ret;
trace_amdgpu_dm_plane_atomic_check(new_plane_state);
dm_plane_state = to_dm_plane_state(new_plane_state);
if (!dm_plane_state->dc_state) return 0;
new_crtc_state = drm_atomic_get_new_crtc_state(state, new_plane_state->crtc); if (!new_crtc_state) return -EINVAL;
ret = amdgpu_dm_plane_helper_check_state(new_plane_state, new_crtc_state); if (ret) return ret;
/* Reject commits that attempt to use both COLOR_PIPELINE and CRTC DEGAMMA_LUT */ if (new_plane_state->color_pipeline && new_crtc_state->degamma_lut) { drm_dbg_atomic(plane->dev, "[PLANE:%d:%s] COLOR_PIPELINE and CRTC DEGAMMA_LUT cannot be enabled simultaneously\n", plane->base.id, plane->name); return -EINVAL; }
ret = amdgpu_dm_plane_fill_dc_scaling_info(adev, new_plane_state, &scaling_info); if (ret) return ret;
if (dc_validate_plane(dc, dm_plane_state->dc_state) == DC_OK) return 0;
return -EINVAL;}EXPORT_IF_KUNIT(amdgpu_dm_plane_atomic_check);
STATIC_IFN_KUNIT int amdgpu_dm_plane_atomic_async_check(struct drm_plane *plane, struct drm_atomic_commit *state, bool flip){ struct drm_crtc_state *new_crtc_state; struct drm_plane_state *new_plane_state; struct dm_crtc_state *dm_new_crtc_state;
if (flip) { if (plane->type != DRM_PLANE_TYPE_OVERLAY) return -EINVAL; } else if (plane->type != DRM_PLANE_TYPE_CURSOR) { return -EINVAL; }
new_plane_state = drm_atomic_get_new_plane_state(state, plane); new_crtc_state = drm_atomic_get_new_crtc_state(state, new_plane_state->crtc); dm_new_crtc_state = to_dm_crtc_state(new_crtc_state); /* Reject overlay cursors for now*/ if (!flip && dm_new_crtc_state->cursor_mode == DM_CURSOR_OVERLAY_MODE) return -EINVAL;
return 0;}EXPORT_IF_KUNIT(amdgpu_dm_plane_atomic_async_check);
int amdgpu_dm_plane_get_cursor_position(struct drm_plane *plane, struct drm_crtc *crtc, struct dc_cursor_position *position){ struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); struct amdgpu_device *adev = drm_to_adev(plane->dev); int x, y; int xorigin = 0, yorigin = 0;
if (!crtc || !plane->state->fb) return 0;
if ((plane->state->crtc_w > amdgpu_crtc->max_cursor_width) || (plane->state->crtc_h > amdgpu_crtc->max_cursor_height)) { DRM_ERROR("%s: bad cursor width or height %d x %d\n", __func__, plane->state->crtc_w, plane->state->crtc_h); return -EINVAL; }
x = plane->state->crtc_x; y = plane->state->crtc_y;
if (x <= -amdgpu_crtc->max_cursor_width || y <= -amdgpu_crtc->max_cursor_height) return 0;
if (x < 0) { xorigin = min(-x, amdgpu_crtc->max_cursor_width - 1); x = 0; } if (y < 0) { yorigin = min(-y, amdgpu_crtc->max_cursor_height - 1); y = 0; } position->enable = true; position->x = x; position->y = y; position->x_hotspot = xorigin; position->y_hotspot = yorigin;
if (amdgpu_ip_version(adev, DCE_HWIP, 0) < IP_VERSION(4, 0, 1)) position->translate_by_source = true;
return 0;}EXPORT_IF_KUNIT(amdgpu_dm_plane_get_cursor_position);
void amdgpu_dm_plane_handle_cursor_update(struct drm_plane *plane, struct drm_plane_state *old_plane_state){ struct amdgpu_device *adev = drm_to_adev(plane->dev); struct amdgpu_framebuffer *afb = to_amdgpu_framebuffer(plane->state->fb); struct drm_crtc *crtc = afb ? plane->state->crtc : old_plane_state->crtc; struct dm_crtc_state *crtc_state = crtc ? to_dm_crtc_state(crtc->state) : NULL; struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); uint64_t address = afb ? afb->address : 0; struct dc_cursor_position position = {0}; struct dc_cursor_attributes attributes; int ret;
if (!plane->state->fb && !old_plane_state->fb) return;
drm_dbg_atomic(plane->dev, "crtc_id=%d with size %d to %d\n", amdgpu_crtc->crtc_id, plane->state->crtc_w, plane->state->crtc_h);
ret = amdgpu_dm_plane_get_cursor_position(plane, crtc, &position); if (ret) return;
if (!position.enable) { /* turn off cursor */ if (crtc_state && crtc_state->stream) { mutex_lock(&adev->dm.dc_lock); amdgpu_dm_ism_commit_event( &amdgpu_crtc->ism, DM_ISM_EVENT_BEGIN_CURSOR_UPDATE);
dc_stream_program_cursor_position(crtc_state->stream, &position);
amdgpu_dm_ism_commit_event( &amdgpu_crtc->ism, DM_ISM_EVENT_END_CURSOR_UPDATE); mutex_unlock(&adev->dm.dc_lock); } return; }
amdgpu_crtc->cursor_width = plane->state->crtc_w; amdgpu_crtc->cursor_height = plane->state->crtc_h;
memset(&attributes, 0, sizeof(attributes)); attributes.address.high_part = upper_32_bits(address); attributes.address.low_part = lower_32_bits(address); attributes.width = plane->state->crtc_w; attributes.height = plane->state->crtc_h; attributes.color_format = CURSOR_MODE_COLOR_PRE_MULTIPLIED_ALPHA; attributes.rotation_angle = 0; attributes.attribute_flags.value = 0;
/* Enable cursor degamma ROM on DCN3+ for implicit sRGB degamma in DRM * legacy gamma setup. */ if (crtc_state->cm_is_degamma_srgb && adev->dm.dc->caps.color.dpp.gamma_corr) attributes.attribute_flags.bits.ENABLE_CURSOR_DEGAMMA = 1;
if (afb) attributes.pitch = afb->base.pitches[0] / afb->base.format->cpp[0];
if (crtc_state->stream) { mutex_lock(&adev->dm.dc_lock); amdgpu_dm_ism_commit_event( &amdgpu_crtc->ism, DM_ISM_EVENT_BEGIN_CURSOR_UPDATE);
if (!dc_stream_program_cursor_attributes(crtc_state->stream, &attributes)) DRM_ERROR("DC failed to set cursor attributes\n");
if (!dc_stream_program_cursor_position(crtc_state->stream, &position)) DRM_ERROR("DC failed to set cursor position\n");
amdgpu_dm_ism_commit_event( &amdgpu_crtc->ism, DM_ISM_EVENT_END_CURSOR_UPDATE); mutex_unlock(&adev->dm.dc_lock); }}EXPORT_IF_KUNIT(amdgpu_dm_plane_handle_cursor_update);
STATIC_IFN_KUNIT void amdgpu_dm_plane_atomic_async_update(struct drm_plane *plane, struct drm_atomic_commit *state){ struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane); struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane);
trace_amdgpu_dm_atomic_update_cursor(new_state);
swap(plane->state->fb, new_state->fb);
plane->state->src_x = new_state->src_x; plane->state->src_y = new_state->src_y; plane->state->src_w = new_state->src_w; plane->state->src_h = new_state->src_h; plane->state->crtc_x = new_state->crtc_x; plane->state->crtc_y = new_state->crtc_y; plane->state->crtc_w = new_state->crtc_w; plane->state->crtc_h = new_state->crtc_h;
amdgpu_dm_plane_handle_cursor_update(plane, old_state);}EXPORT_IF_KUNIT(amdgpu_dm_plane_atomic_async_update);
STATIC_IFN_KUNIT void amdgpu_dm_plane_panic_flush(struct drm_plane *plane){ struct dm_plane_state *dm_plane_state = to_dm_plane_state(plane->state); struct drm_framebuffer *fb = plane->state->fb; struct dc_plane_state *dc_plane_state;
if (!dm_plane_state || !dm_plane_state->dc_state) return;
dc_plane_state = dm_plane_state->dc_state;
dc_plane_force_dcc_and_tiling_disable(dc_plane_state, fb->modifier ? true : false);}EXPORT_IF_KUNIT(amdgpu_dm_plane_panic_flush);
static const struct drm_plane_helper_funcs dm_plane_helper_funcs = { .prepare_fb = amdgpu_dm_plane_helper_prepare_fb, .cleanup_fb = amdgpu_dm_plane_helper_cleanup_fb, .atomic_check = amdgpu_dm_plane_atomic_check, .atomic_async_check = amdgpu_dm_plane_atomic_async_check, .atomic_async_update = amdgpu_dm_plane_atomic_async_update};
static const struct drm_plane_helper_funcs dm_primary_plane_helper_funcs = { .prepare_fb = amdgpu_dm_plane_helper_prepare_fb, .cleanup_fb = amdgpu_dm_plane_helper_cleanup_fb, .atomic_check = amdgpu_dm_plane_atomic_check, .atomic_async_check = amdgpu_dm_plane_atomic_async_check, .atomic_async_update = amdgpu_dm_plane_atomic_async_update, .get_scanout_buffer = amdgpu_display_get_scanout_buffer, .panic_flush = amdgpu_dm_plane_panic_flush,};
STATIC_IFN_KUNIT struct drm_plane_state *amdgpu_dm_plane_drm_plane_create_state(struct drm_plane *plane){ struct dm_plane_state *amdgpu_state;
amdgpu_state = kzalloc_obj(*amdgpu_state); if (!amdgpu_state) return ERR_PTR(-ENOMEM);
amdgpu_state->flip_addr = kzalloc_obj(*amdgpu_state->flip_addr); amdgpu_state->scaling_info = kzalloc_obj(*amdgpu_state->scaling_info); amdgpu_state->plane_info = kzalloc_obj(*amdgpu_state->plane_info); if (!amdgpu_state->flip_addr || !amdgpu_state->scaling_info || !amdgpu_state->plane_info) { kfree(amdgpu_state->flip_addr); kfree(amdgpu_state->scaling_info); kfree(amdgpu_state->plane_info); kfree(amdgpu_state); return ERR_PTR(-ENOMEM); }
if (plane->state) plane->funcs->atomic_destroy_state(plane, plane->state);
__drm_atomic_helper_plane_state_init(&amdgpu_state->base, plane); amdgpu_state->degamma_tf = AMDGPU_TRANSFER_FUNCTION_DEFAULT; amdgpu_state->hdr_mult = AMDGPU_HDR_MULT_DEFAULT; amdgpu_state->shaper_tf = AMDGPU_TRANSFER_FUNCTION_DEFAULT; amdgpu_state->blend_tf = AMDGPU_TRANSFER_FUNCTION_DEFAULT;
return &amdgpu_state->base;}EXPORT_IF_KUNIT(amdgpu_dm_plane_drm_plane_create_state);
STATIC_IFN_KUNIT struct drm_plane_state *amdgpu_dm_plane_drm_plane_duplicate_state(struct drm_plane *plane){ struct dm_plane_state *dm_plane_state, *old_dm_plane_state;
old_dm_plane_state = to_dm_plane_state(plane->state); dm_plane_state = kzalloc_obj(*dm_plane_state); if (!dm_plane_state) return NULL;
dm_plane_state->flip_addr = kmemdup(old_dm_plane_state->flip_addr, sizeof(*old_dm_plane_state->flip_addr), GFP_KERNEL); dm_plane_state->scaling_info = kmemdup(old_dm_plane_state->scaling_info, sizeof(*old_dm_plane_state->scaling_info), GFP_KERNEL); dm_plane_state->plane_info = kmemdup(old_dm_plane_state->plane_info, sizeof(*old_dm_plane_state->plane_info), GFP_KERNEL); if (!dm_plane_state->flip_addr || !dm_plane_state->scaling_info || !dm_plane_state->plane_info) { kfree(dm_plane_state->flip_addr); kfree(dm_plane_state->scaling_info); kfree(dm_plane_state->plane_info); kfree(dm_plane_state); return NULL; }
__drm_atomic_helper_plane_duplicate_state(plane, &dm_plane_state->base);
if (old_dm_plane_state->dc_state) { dm_plane_state->dc_state = old_dm_plane_state->dc_state; dc_plane_state_retain(dm_plane_state->dc_state); }
if (old_dm_plane_state->degamma_lut) dm_plane_state->degamma_lut = drm_property_blob_get(old_dm_plane_state->degamma_lut); if (old_dm_plane_state->ctm) dm_plane_state->ctm = drm_property_blob_get(old_dm_plane_state->ctm); if (old_dm_plane_state->shaper_lut) dm_plane_state->shaper_lut = drm_property_blob_get(old_dm_plane_state->shaper_lut); if (old_dm_plane_state->lut3d) dm_plane_state->lut3d = drm_property_blob_get(old_dm_plane_state->lut3d); if (old_dm_plane_state->blend_lut) dm_plane_state->blend_lut = drm_property_blob_get(old_dm_plane_state->blend_lut);
dm_plane_state->degamma_tf = old_dm_plane_state->degamma_tf; dm_plane_state->hdr_mult = old_dm_plane_state->hdr_mult; dm_plane_state->shaper_tf = old_dm_plane_state->shaper_tf; dm_plane_state->blend_tf = old_dm_plane_state->blend_tf;
return &dm_plane_state->base;}EXPORT_IF_KUNIT(amdgpu_dm_plane_drm_plane_duplicate_state);
STATIC_IFN_KUNIT bool amdgpu_dm_plane_format_mod_supported(struct drm_plane *plane, uint32_t format, uint64_t modifier){ struct amdgpu_device *adev = drm_to_adev(plane->dev); const struct drm_format_info *info = drm_format_info(format); const u32 bpp = drm_format_info_bpp(info, 0); int i;
if (!info) return false;
/* * We always have to allow these modifiers: * 1. Core DRM checks for LINEAR support if userspace does not provide modifiers. * 2. Not passing any modifiers is the same as explicitly passing INVALID. */ if (modifier == DRM_FORMAT_MOD_LINEAR || modifier == DRM_FORMAT_MOD_INVALID) { return true; }
/* Check that the modifier is on the list of the plane's supported modifiers. */ for (i = 0; i < plane->modifier_count; i++) { if (modifier == plane->modifiers[i]) break; } if (i == plane->modifier_count) return false;
if (AMD_FMT_MOD_GET(TILE_VERSION, modifier) == AMD_FMT_MOD_TILE_VER_GFX6) return amdgpu_dm_plane_gfx6_format_mod_supported(adev, bpp, modifier);
/* GFX9+ modifers are not supported on GFX8 and older. */ if (adev->family < AMDGPU_FAMILY_AI) return false;
/* GFX12 doesn't have these limitations. */ if (AMD_FMT_MOD_GET(TILE_VERSION, modifier) <= AMD_FMT_MOD_TILE_VER_GFX11) { enum dm_micro_swizzle microtile = amdgpu_dm_plane_modifier_gfx9_swizzle_mode(modifier) & 3;
/* * For D swizzle the canonical modifier depends on the bpp, so check * it here. */ if (AMD_FMT_MOD_GET(TILE_VERSION, modifier) == AMD_FMT_MOD_TILE_VER_GFX9 && adev->family >= AMDGPU_FAMILY_NV) { if (microtile == MICRO_SWIZZLE_D && info->cpp[0] == 4) return false; }
if (adev->family >= AMDGPU_FAMILY_RV && microtile == MICRO_SWIZZLE_D && info->cpp[0] < 8) return false;
if (amdgpu_dm_plane_modifier_has_dcc(modifier)) { /* Per radeonsi comments 16/64 bpp are more complicated. */ if (info->cpp[0] != 4) return false; /* We support multi-planar formats, but not when combined with * additional DCC metadata planes. */ if (info->num_planes > 1) return false; } }
return true;}EXPORT_IF_KUNIT(amdgpu_dm_plane_format_mod_supported);
STATIC_IFN_KUNIT void amdgpu_dm_plane_drm_plane_destroy_state(struct drm_plane *plane, struct drm_plane_state *state){ struct dm_plane_state *dm_plane_state = to_dm_plane_state(state);
if (dm_plane_state->degamma_lut) drm_property_blob_put(dm_plane_state->degamma_lut); if (dm_plane_state->ctm) drm_property_blob_put(dm_plane_state->ctm); if (dm_plane_state->lut3d) drm_property_blob_put(dm_plane_state->lut3d); if (dm_plane_state->shaper_lut) drm_property_blob_put(dm_plane_state->shaper_lut); if (dm_plane_state->blend_lut) drm_property_blob_put(dm_plane_state->blend_lut);
kfree(dm_plane_state->flip_addr); kfree(dm_plane_state->scaling_info); kfree(dm_plane_state->plane_info);
if (dm_plane_state->dc_state) dc_plane_state_release(dm_plane_state->dc_state);
drm_atomic_helper_plane_destroy_state(plane, state);}EXPORT_IF_KUNIT(amdgpu_dm_plane_drm_plane_destroy_state);
#ifdef AMD_PRIVATE_COLORstatic voiddm_atomic_plane_attach_color_mgmt_properties(struct amdgpu_display_manager *dm, struct drm_plane *plane){ struct amdgpu_mode_info mode_info = dm->adev->mode_info; struct dpp_color_caps dpp_color_caps = dm->dc->caps.color.dpp;
/* Check HW color pipeline capabilities on DPP block (pre-blending) * before exposing related properties. */ if (dpp_color_caps.dgam_ram || dpp_color_caps.gamma_corr) { drm_object_attach_property(&plane->base, mode_info.plane_degamma_lut_property, 0); drm_object_attach_property(&plane->base, mode_info.plane_degamma_lut_size_property, MAX_COLOR_LUT_ENTRIES); drm_object_attach_property(&plane->base, dm->adev->mode_info.plane_degamma_tf_property, AMDGPU_TRANSFER_FUNCTION_DEFAULT); } /* HDR MULT is always available */ drm_object_attach_property(&plane->base, dm->adev->mode_info.plane_hdr_mult_property, AMDGPU_HDR_MULT_DEFAULT);
/* Only enable plane CTM if both DPP and MPC gamut remap is available. */ if (dm->dc->caps.color.mpc.gamut_remap) drm_object_attach_property(&plane->base, dm->adev->mode_info.plane_ctm_property, 0);
if (dpp_color_caps.hw_3d_lut || dm->dc->caps.color.mpc.preblend) { drm_object_attach_property(&plane->base, mode_info.plane_shaper_lut_property, 0); drm_object_attach_property(&plane->base, mode_info.plane_shaper_lut_size_property, MAX_COLOR_LUT_ENTRIES); drm_object_attach_property(&plane->base, mode_info.plane_shaper_tf_property, AMDGPU_TRANSFER_FUNCTION_DEFAULT); drm_object_attach_property(&plane->base, mode_info.plane_lut3d_property, 0); drm_object_attach_property(&plane->base, mode_info.plane_lut3d_size_property, MAX_COLOR_3DLUT_SIZE); }
if (dpp_color_caps.ogam_ram || dm->dc->caps.color.mpc.preblend) { drm_object_attach_property(&plane->base, mode_info.plane_blend_lut_property, 0); drm_object_attach_property(&plane->base, mode_info.plane_blend_lut_size_property, MAX_COLOR_LUT_ENTRIES); drm_object_attach_property(&plane->base, mode_info.plane_blend_tf_property, AMDGPU_TRANSFER_FUNCTION_DEFAULT); }}
static intdm_atomic_plane_set_property(struct drm_plane *plane, struct drm_plane_state *state, struct drm_property *property, uint64_t val){ struct dm_plane_state *dm_plane_state = to_dm_plane_state(state); struct amdgpu_device *adev = drm_to_adev(plane->dev); bool replaced = false; int ret;
if (property == adev->mode_info.plane_degamma_lut_property) { ret = drm_property_replace_blob_from_id(plane->dev, &dm_plane_state->degamma_lut, val, -1, -1, sizeof(struct drm_color_lut), &replaced); dm_plane_state->base.color_mgmt_changed |= replaced; return ret; } else if (property == adev->mode_info.plane_degamma_tf_property) { if (dm_plane_state->degamma_tf != val) { dm_plane_state->degamma_tf = val; dm_plane_state->base.color_mgmt_changed = 1; } } else if (property == adev->mode_info.plane_hdr_mult_property) { if (dm_plane_state->hdr_mult != val) { dm_plane_state->hdr_mult = val; dm_plane_state->base.color_mgmt_changed = 1; } } else if (property == adev->mode_info.plane_ctm_property) { ret = drm_property_replace_blob_from_id(plane->dev, &dm_plane_state->ctm, val, -1, sizeof(struct drm_color_ctm_3x4), -1, &replaced); dm_plane_state->base.color_mgmt_changed |= replaced; return ret; } else if (property == adev->mode_info.plane_shaper_lut_property) { ret = drm_property_replace_blob_from_id(plane->dev, &dm_plane_state->shaper_lut, val, -1, -1, sizeof(struct drm_color_lut), &replaced); dm_plane_state->base.color_mgmt_changed |= replaced; return ret; } else if (property == adev->mode_info.plane_shaper_tf_property) { if (dm_plane_state->shaper_tf != val) { dm_plane_state->shaper_tf = val; dm_plane_state->base.color_mgmt_changed = 1; } } else if (property == adev->mode_info.plane_lut3d_property) { ret = drm_property_replace_blob_from_id(plane->dev, &dm_plane_state->lut3d, val, -1, -1, sizeof(struct drm_color_lut), &replaced); dm_plane_state->base.color_mgmt_changed |= replaced; return ret; } else if (property == adev->mode_info.plane_blend_lut_property) { ret = drm_property_replace_blob_from_id(plane->dev, &dm_plane_state->blend_lut, val, -1, -1, sizeof(struct drm_color_lut), &replaced); dm_plane_state->base.color_mgmt_changed |= replaced; return ret; } else if (property == adev->mode_info.plane_blend_tf_property) { if (dm_plane_state->blend_tf != val) { dm_plane_state->blend_tf = val; dm_plane_state->base.color_mgmt_changed = 1; } } else { drm_dbg_atomic(plane->dev, "[PLANE:%d:%s] unknown property [PROP:%d:%s]]\n", plane->base.id, plane->name, property->base.id, property->name); return -EINVAL; }
return 0;}
static intdm_atomic_plane_get_property(struct drm_plane *plane, const struct drm_plane_state *state, struct drm_property *property, uint64_t *val){ struct dm_plane_state *dm_plane_state = to_dm_plane_state(state); struct amdgpu_device *adev = drm_to_adev(plane->dev);
if (property == adev->mode_info.plane_degamma_lut_property) { *val = (dm_plane_state->degamma_lut) ? dm_plane_state->degamma_lut->base.id : 0; } else if (property == adev->mode_info.plane_degamma_tf_property) { *val = dm_plane_state->degamma_tf; } else if (property == adev->mode_info.plane_hdr_mult_property) { *val = dm_plane_state->hdr_mult; } else if (property == adev->mode_info.plane_ctm_property) { *val = (dm_plane_state->ctm) ? dm_plane_state->ctm->base.id : 0; } else if (property == adev->mode_info.plane_shaper_lut_property) { *val = (dm_plane_state->shaper_lut) ? dm_plane_state->shaper_lut->base.id : 0; } else if (property == adev->mode_info.plane_shaper_tf_property) { *val = dm_plane_state->shaper_tf; } else if (property == adev->mode_info.plane_lut3d_property) { *val = (dm_plane_state->lut3d) ? dm_plane_state->lut3d->base.id : 0; } else if (property == adev->mode_info.plane_blend_lut_property) { *val = (dm_plane_state->blend_lut) ? dm_plane_state->blend_lut->base.id : 0; } else if (property == adev->mode_info.plane_blend_tf_property) { *val = dm_plane_state->blend_tf;
} else { return -EINVAL; }
return 0;}#else
#define MAX_COLOR_PIPELINES 5
static intdm_plane_init_colorops(struct drm_plane *plane){ struct drm_prop_enum_list pipelines[MAX_COLOR_PIPELINES] = {}; struct drm_device *dev = plane->dev; struct amdgpu_device *adev = drm_to_adev(dev); struct dc *dc = adev->dm.dc; int len = 0; int ret = 0; int i;
if (plane->type == DRM_PLANE_TYPE_CURSOR) return 0;
/* initialize pipeline */ if (dc->ctx->dce_version >= DCN_VERSION_3_0) { ret = amdgpu_dm_initialize_default_pipeline(plane, &pipelines[len]); if (ret) { drm_err(plane->dev, "Failed to create color pipeline for plane %d: %pe\n", plane->base.id, ERR_PTR(ret)); goto out; } len++;
/* Create COLOR_PIPELINE property and attach */ drm_plane_create_color_pipeline_property(plane, pipelines, len); }
out: for (i = 0; i < len; i++) kfree(pipelines[i].name);
return ret;}#endif
static const struct drm_plane_funcs dm_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, .destroy = drm_plane_helper_destroy, .atomic_create_state = amdgpu_dm_plane_drm_plane_create_state, .atomic_duplicate_state = amdgpu_dm_plane_drm_plane_duplicate_state, .atomic_destroy_state = amdgpu_dm_plane_drm_plane_destroy_state, .format_mod_supported = amdgpu_dm_plane_format_mod_supported, .format_mod_supported_async = amdgpu_dm_plane_format_mod_supported,#ifdef AMD_PRIVATE_COLOR .atomic_set_property = dm_atomic_plane_set_property, .atomic_get_property = dm_atomic_plane_get_property,#endif DRM_PANIC_PLANE_FUNCS,};
int amdgpu_dm_plane_init(struct amdgpu_display_manager *dm, struct drm_plane *plane, unsigned long possible_crtcs, const struct dc_plane_cap *plane_cap){ uint32_t formats[32]; int num_formats; int res = -EPERM; unsigned int supported_rotations; uint64_t *modifiers = NULL; unsigned int primary_zpos = dm->dc->caps.max_slave_planes;
num_formats = amdgpu_dm_plane_get_plane_formats(plane, plane_cap, formats, ARRAY_SIZE(formats));
res = amdgpu_dm_plane_get_plane_modifiers(dm->adev, plane->type, &modifiers); if (res) return res;
if (modifiers == NULL) adev_to_drm(dm->adev)->mode_config.fb_modifiers_not_supported = true;
res = drm_universal_plane_init(adev_to_drm(dm->adev), plane, possible_crtcs, &dm_plane_funcs, formats, num_formats, modifiers, plane->type, NULL); kfree(modifiers); if (res) return res;
/* Blend mode support varies on DCE generations according to HW caps * and number of planes per CRTC. However, as current driver * implementation only creates one primary and one cursor plane per * CRTC for DCE (overlay is only created if * DC_PLANE_TYPE_DCN_UNIVERSAL), the primary plane blend mode is * ignored across DCE versions. Keep PREMULTI to avoid uAPI * regressions: it was the default/mandatory mode for many years and, * with no overlay plane, primary composes on top of a black * background, where PREMULTI and PIXEL_NONE are equivalent. */ if (plane_cap && plane_cap->type != DC_PLANE_TYPE_DCN_UNIVERSAL) { unsigned int blend_caps = BIT(DRM_MODE_BLEND_PIXEL_NONE) | BIT(DRM_MODE_BLEND_PREMULTI);
drm_plane_create_blend_mode_property(plane, blend_caps); } else if ((plane->type == DRM_PLANE_TYPE_OVERLAY || plane->type == DRM_PLANE_TYPE_PRIMARY) && plane_cap && plane_cap->per_pixel_alpha) { unsigned int blend_caps = BIT(DRM_MODE_BLEND_PIXEL_NONE) | BIT(DRM_MODE_BLEND_PREMULTI) | BIT(DRM_MODE_BLEND_COVERAGE);
drm_plane_create_blend_mode_property(plane, blend_caps);
if (plane->type == DRM_PLANE_TYPE_OVERLAY) drm_plane_create_alpha_property(plane); }
/* Cursor color format is set to CURSOR_MODE_COLOR_PRE_MULTIPLIED_ALPHA * by default, so only advertise DRM_MODE_BLEND_PREMULTI blend mode for * this type of plane. */ if (plane->type == DRM_PLANE_TYPE_CURSOR) drm_plane_create_blend_mode_property(plane, BIT(DRM_MODE_BLEND_PREMULTI));
if (plane->type == DRM_PLANE_TYPE_PRIMARY) { /* * Allow OVERLAY planes to be used as underlays by assigning an * immutable zpos = # of OVERLAY planes to the PRIMARY plane. */ drm_plane_create_zpos_immutable_property(plane, primary_zpos); } else if (plane->type == DRM_PLANE_TYPE_OVERLAY) { /* * OVERLAY planes can be below or above the PRIMARY, but cannot * be above the CURSOR plane. */ unsigned int zpos = primary_zpos + 1 + drm_plane_index(plane);
drm_plane_create_zpos_property(plane, zpos, 0, 254); } else if (plane->type == DRM_PLANE_TYPE_CURSOR) { drm_plane_create_zpos_immutable_property(plane, 255); }
if ((plane->type == DRM_PLANE_TYPE_PRIMARY || plane->type == DRM_PLANE_TYPE_OVERLAY) && plane_cap && (plane_cap->pixel_format_support.nv12 || plane_cap->pixel_format_support.p010)) { /* This only affects YUV formats. */ drm_plane_create_color_properties( plane, BIT(DRM_COLOR_YCBCR_BT601) | BIT(DRM_COLOR_YCBCR_BT709) | BIT(DRM_COLOR_YCBCR_BT2020), BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) | BIT(DRM_COLOR_YCBCR_FULL_RANGE), DRM_COLOR_YCBCR_BT709, DRM_COLOR_YCBCR_LIMITED_RANGE); }
supported_rotations = DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_180 | DRM_MODE_ROTATE_270;
if (dm->adev->asic_type >= CHIP_BONAIRE && plane->type != DRM_PLANE_TYPE_CURSOR) drm_plane_create_rotation_property(plane, DRM_MODE_ROTATE_0, supported_rotations);
if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) > IP_VERSION(3, 0, 1) && plane->type != DRM_PLANE_TYPE_CURSOR) drm_plane_enable_fb_damage_clips(plane);
if (plane->type == DRM_PLANE_TYPE_PRIMARY) drm_plane_helper_add(plane, &dm_primary_plane_helper_funcs); else drm_plane_helper_add(plane, &dm_plane_helper_funcs);
#ifdef AMD_PRIVATE_COLOR dm_atomic_plane_attach_color_mgmt_properties(dm, plane);#else res = dm_plane_init_colorops(plane); if (res) return res;#endif
/* Create the plane state */ if (plane->funcs->atomic_create_state) { struct drm_plane_state *plane_state;
plane_state = plane->funcs->atomic_create_state(plane); if (IS_ERR(plane_state)) return PTR_ERR(plane_state);
plane->state = plane_state; }
return 0;}EXPORT_IF_KUNIT(amdgpu_dm_plane_init);
bool amdgpu_dm_plane_is_video_format(uint32_t format){ int i;
for (i = 0; i < ARRAY_SIZE(video_formats); i++) if (format == video_formats[i]) return true;
return false;}EXPORT_IF_KUNIT(amdgpu_dm_plane_is_video_format);