···2727#include "amdgpu_dm.h"2828#include "dc.h"2929#include "modules/color/color_gamma.h"3030-#include "basics/conversion.h"3131-3232-/*3333- * The DC interface to HW gives us the following color management blocks3434- * per pipe (surface):3535- *3636- * - Input gamma LUT (de-normalized)3737- * - Input CSC (normalized)3838- * - Surface degamma LUT (normalized)3939- * - Surface CSC (normalized)4040- * - Surface regamma LUT (normalized)4141- * - Output CSC (normalized)4242- *4343- * But these aren't a direct mapping to DRM color properties. The current DRM4444- * interface exposes CRTC degamma, CRTC CTM and CRTC regamma while our hardware4545- * is essentially giving:4646- *4747- * Plane CTM -> Plane degamma -> Plane CTM -> Plane regamma -> Plane CTM4848- *4949- * The input gamma LUT block isn't really applicable here since it operates5050- * on the actual input data itself rather than the HW fp representation. The5151- * input and output CSC blocks are technically available to use as part of5252- * the DC interface but are typically used internally by DC for conversions5353- * between color spaces. These could be blended together with user5454- * adjustments in the future but for now these should remain untouched.5555- *5656- * The pipe blending also happens after these blocks so we don't actually5757- * support any CRTC props with correct blending with multiple planes - but we5858- * can still support CRTC color management properties in DM in most single5959- * plane cases correctly with clever management of the DC interface in DM.6060- *6161- * As per DRM documentation, blocks should be in hardware bypass when their6262- * respective property is set to NULL. A linear DGM/RGM LUT should also6363- * considered as putting the respective block into bypass mode.6464- *6565- * This means that the following6666- * configuration is assumed to be the default:6767- *6868- * Plane DGM Bypass -> Plane CTM Bypass -> Plane RGM Bypass -> ...6969- * CRTC DGM Bypass -> CRTC CTM Bypass -> CRTC RGM Bypass7070- */71307231#define MAX_DRM_LUT_VALUE 0xFFFF7332···4182 setup_x_points_distribution();4283}43844444-/* Extracts the DRM lut and lut size from a blob. */4545-static const struct drm_color_lut *4646-__extract_blob_lut(const struct drm_property_blob *blob, uint32_t *size)4747-{4848- *size = blob ? drm_color_lut_size(blob) : 0;4949- return blob ? (struct drm_color_lut *)blob->data : NULL;5050-}51855286/*5387 * Return true if the given lut is a linear mapping of values, i.e. it acts···5098 * f(a) = (0xFF00/MAX_COLOR_LUT_ENTRIES-1)a; for integer a in5199 * [0, MAX_COLOR_LUT_ENTRIES)52100 */5353-static bool __is_lut_linear(const struct drm_color_lut *lut, uint32_t size)101101+static bool __is_lut_linear(struct drm_color_lut *lut, uint32_t size)54102{55103 int i;56104 uint32_t expected;···75123 * Convert the drm_color_lut to dc_gamma. The conversion depends on the size76124 * of the lut - whether or not it's legacy.77125 */7878-static void __drm_lut_to_dc_gamma(const struct drm_color_lut *lut,7979- struct dc_gamma *gamma, bool is_legacy)126126+static void __drm_lut_to_dc_gamma(struct drm_color_lut *lut,127127+ struct dc_gamma *gamma,128128+ bool is_legacy)80129{81130 uint32_t r, g, b;82131 int i;···107154 }108155}109156110110-/*111111- * Converts a DRM CTM to a DC CSC float matrix.112112- * The matrix needs to be a 3x4 (12 entry) matrix.157157+/**158158+ * amdgpu_dm_set_regamma_lut: Set regamma lut for the given CRTC.159159+ * @crtc: amdgpu_dm crtc state160160+ *161161+ * Update the underlying dc_stream_state's output transfer function (OTF) in162162+ * preparation for hardware commit. If no lut is specified by user, we default163163+ * to SRGB.164164+ *165165+ * RETURNS:166166+ * 0 on success, -ENOMEM if memory cannot be allocated to calculate the OTF.113167 */114114-static void __drm_ctm_to_dc_matrix(const struct drm_color_ctm *ctm,115115- struct fixed31_32 *matrix)168168+int amdgpu_dm_set_regamma_lut(struct dm_crtc_state *crtc)116169{170170+ struct drm_property_blob *blob = crtc->base.gamma_lut;171171+ struct dc_stream_state *stream = crtc->stream;172172+ struct amdgpu_device *adev = (struct amdgpu_device *)173173+ crtc->base.state->dev->dev_private;174174+ struct drm_color_lut *lut;175175+ uint32_t lut_size;176176+ struct dc_gamma *gamma = NULL;177177+ enum dc_transfer_func_type old_type = stream->out_transfer_func->type;178178+179179+ bool ret;180180+181181+ if (!blob && adev->asic_type <= CHIP_RAVEN) {182182+ /* By default, use the SRGB predefined curve.*/183183+ stream->out_transfer_func->type = TF_TYPE_PREDEFINED;184184+ stream->out_transfer_func->tf = TRANSFER_FUNCTION_SRGB;185185+ return 0;186186+ }187187+188188+ if (blob) {189189+ lut = (struct drm_color_lut *)blob->data;190190+ lut_size = blob->length / sizeof(struct drm_color_lut);191191+192192+ gamma = dc_create_gamma();193193+ if (!gamma)194194+ return -ENOMEM;195195+196196+ gamma->num_entries = lut_size;197197+ if (gamma->num_entries == MAX_COLOR_LEGACY_LUT_ENTRIES)198198+ gamma->type = GAMMA_RGB_256;199199+ else if (gamma->num_entries == MAX_COLOR_LUT_ENTRIES)200200+ gamma->type = GAMMA_CS_TFM_1D;201201+ else {202202+ /* Invalid lut size */203203+ dc_gamma_release(&gamma);204204+ return -EINVAL;205205+ }206206+207207+ /* Convert drm_lut into dc_gamma */208208+ __drm_lut_to_dc_gamma(lut, gamma, gamma->type == GAMMA_RGB_256);209209+ }210210+211211+ /* predefined gamma ROM only exist for RAVEN and pre-RAVEN ASIC,212212+ * set canRomBeUsed accordingly213213+ */214214+ stream->out_transfer_func->type = TF_TYPE_DISTRIBUTED_POINTS;215215+ ret = mod_color_calculate_regamma_params(stream->out_transfer_func,216216+ gamma, true, adev->asic_type <= CHIP_RAVEN, NULL);217217+218218+ if (gamma)219219+ dc_gamma_release(&gamma);220220+221221+ if (!ret) {222222+ stream->out_transfer_func->type = old_type;223223+ DRM_ERROR("Out of memory when calculating regamma params\n");224224+ return -ENOMEM;225225+ }226226+227227+ return 0;228228+}229229+230230+/**231231+ * amdgpu_dm_set_ctm: Set the color transform matrix for the given CRTC.232232+ * @crtc: amdgpu_dm crtc state233233+ *234234+ * Update the underlying dc_stream_state's gamut remap matrix in preparation235235+ * for hardware commit. If no matrix is specified by user, gamut remap will be236236+ * disabled.237237+ */238238+void amdgpu_dm_set_ctm(struct dm_crtc_state *crtc)239239+{240240+241241+ struct drm_property_blob *blob = crtc->base.ctm;242242+ struct dc_stream_state *stream = crtc->stream;243243+ struct drm_color_ctm *ctm;117244 int64_t val;118245 int i;119246247247+ if (!blob) {248248+ stream->gamut_remap_matrix.enable_remap = false;249249+ return;250250+ }251251+252252+ stream->gamut_remap_matrix.enable_remap = true;253253+ ctm = (struct drm_color_ctm *)blob->data;120254 /*121255 * DRM gives a 3x3 matrix, but DC wants 3x4. Assuming we're operating122256 * with homogeneous coordinates, augment the matrix with 0's.···215175 for (i = 0; i < 12; i++) {216176 /* Skip 4th element */217177 if (i % 4 == 3) {218218- matrix[i] = dc_fixpt_zero;178178+ stream->gamut_remap_matrix.matrix[i] = dc_fixpt_zero;219179 continue;220180 }221181222182 /* gamut_remap_matrix[i] = ctm[i - floor(i/4)] */223223- val = ctm->matrix[i - (i / 4)];183183+ val = ctm->matrix[i - (i/4)];224184 /* If negative, convert to 2's complement. */225185 if (val & (1ULL << 63))226186 val = -(val & ~(1ULL << 63));227187228228- matrix[i].value = val;188188+ stream->gamut_remap_matrix.matrix[i].value = val;229189 }230190}231191232232-/* Calculates the legacy transfer function - only for sRGB input space. */233233-static int __set_legacy_tf(struct dc_transfer_func *func,234234- const struct drm_color_lut *lut, uint32_t lut_size,235235- bool has_rom)236236-{237237- struct dc_gamma *gamma = NULL;238238- bool res;239239-240240- ASSERT(lut && lut_size == MAX_COLOR_LEGACY_LUT_ENTRIES);241241-242242- gamma = dc_create_gamma();243243- if (!gamma)244244- return -ENOMEM;245245-246246- gamma->type = GAMMA_RGB_256;247247- gamma->num_entries = lut_size;248248- __drm_lut_to_dc_gamma(lut, gamma, true);249249-250250- res = mod_color_calculate_regamma_params(func, gamma, true, has_rom,251251- NULL);252252-253253- return res ? 0 : -ENOMEM;254254-}255255-256256-/* Calculates the output transfer function based on expected input space. */257257-static int __set_output_tf(struct dc_transfer_func *func,258258- const struct drm_color_lut *lut, uint32_t lut_size,259259- bool has_rom)260260-{261261- struct dc_gamma *gamma = NULL;262262- bool res;263263-264264- ASSERT(lut && lut_size == MAX_COLOR_LUT_ENTRIES);265265-266266- gamma = dc_create_gamma();267267- if (!gamma)268268- return -ENOMEM;269269-270270- gamma->num_entries = lut_size;271271- __drm_lut_to_dc_gamma(lut, gamma, false);272272-273273- if (func->tf == TRANSFER_FUNCTION_LINEAR) {274274- /*275275- * Color module doesn't like calculating regamma params276276- * on top of a linear input. But degamma params can be used277277- * instead to simulate this.278278- */279279- gamma->type = GAMMA_CUSTOM;280280- res = mod_color_calculate_degamma_params(func, gamma, true);281281- } else {282282- /*283283- * Assume sRGB. The actual mapping will depend on whether the284284- * input was legacy or not.285285- */286286- gamma->type = GAMMA_CS_TFM_1D;287287- res = mod_color_calculate_regamma_params(func, gamma, false,288288- has_rom, NULL);289289- }290290-291291- dc_gamma_release(&gamma);292292-293293- return res ? 0 : -ENOMEM;294294-}295295-296296-/* Caculates the input transfer function based on expected input space. */297297-static int __set_input_tf(struct dc_transfer_func *func,298298- const struct drm_color_lut *lut, uint32_t lut_size)299299-{300300- struct dc_gamma *gamma = NULL;301301- bool res;302302-303303- gamma = dc_create_gamma();304304- if (!gamma)305305- return -ENOMEM;306306-307307- gamma->type = GAMMA_CUSTOM;308308- gamma->num_entries = lut_size;309309-310310- __drm_lut_to_dc_gamma(lut, gamma, false);311311-312312- res = mod_color_calculate_degamma_params(func, gamma, true);313313- dc_gamma_release(&gamma);314314-315315- return res ? 0 : -ENOMEM;316316-}317192318193/**319319- * amdgpu_dm_update_crtc_color_mgmt: Maps DRM color management to DC stream.194194+ * amdgpu_dm_set_degamma_lut: Set degamma lut for the given CRTC.320195 * @crtc: amdgpu_dm crtc state321321- *322322- * With no plane level color management properties we're free to use any323323- * of the HW blocks as long as the CRTC CTM always comes before the324324- * CRTC RGM and after the CRTC DGM.325325- *326326- * The CRTC RGM block will be placed in the RGM LUT block if it is non-linear.327327- * The CRTC DGM block will be placed in the DGM LUT block if it is non-linear.328328- * The CRTC CTM will be placed in the gamut remap block if it is non-linear.329329- *330330- * The RGM block is typically more fully featured and accurate across331331- * all ASICs - DCE can't support a custom non-linear CRTC DGM.332332- *333333- * For supporting both plane level color management and CRTC level color334334- * management at once we have to either restrict the usage of CRTC properties335335- * or blend adjustments together.336336- *337337- * Returns 0 on success.338338- */339339-int amdgpu_dm_update_crtc_color_mgmt(struct dm_crtc_state *crtc)340340-{341341- struct dc_stream_state *stream = crtc->stream;342342- struct amdgpu_device *adev =343343- (struct amdgpu_device *)crtc->base.state->dev->dev_private;344344- bool has_rom = adev->asic_type <= CHIP_RAVEN;345345- struct drm_color_ctm *ctm = NULL;346346- const struct drm_color_lut *degamma_lut, *regamma_lut;347347- uint32_t degamma_size, regamma_size;348348- bool has_regamma, has_degamma;349349- bool is_legacy;350350- int r;351351-352352- degamma_lut = __extract_blob_lut(crtc->base.degamma_lut, °amma_size);353353- if (degamma_lut && degamma_size != MAX_COLOR_LUT_ENTRIES)354354- return -EINVAL;355355-356356- regamma_lut = __extract_blob_lut(crtc->base.gamma_lut, ®amma_size);357357- if (regamma_lut && regamma_size != MAX_COLOR_LUT_ENTRIES &&358358- regamma_size != MAX_COLOR_LEGACY_LUT_ENTRIES)359359- return -EINVAL;360360-361361- has_degamma =362362- degamma_lut && !__is_lut_linear(degamma_lut, degamma_size);363363-364364- has_regamma =365365- regamma_lut && !__is_lut_linear(regamma_lut, regamma_size);366366-367367- is_legacy = regamma_size == MAX_COLOR_LEGACY_LUT_ENTRIES;368368-369369- /* Reset all adjustments. */370370- crtc->cm_has_degamma = false;371371- crtc->cm_is_degamma_srgb = false;372372-373373- /* Setup regamma and degamma. */374374- if (is_legacy) {375375- /*376376- * Legacy regamma forces us to use the sRGB RGM as a base.377377- * This also means we can't use linear DGM since DGM needs378378- * to use sRGB as a base as well, resulting in incorrect CRTC379379- * DGM and CRTC CTM.380380- *381381- * TODO: Just map this to the standard regamma interface382382- * instead since this isn't really right. One of the cases383383- * where this setup currently fails is trying to do an384384- * inverse color ramp in legacy userspace.385385- */386386- crtc->cm_is_degamma_srgb = true;387387- stream->out_transfer_func->type = TF_TYPE_DISTRIBUTED_POINTS;388388- stream->out_transfer_func->tf = TRANSFER_FUNCTION_SRGB;389389-390390- r = __set_legacy_tf(stream->out_transfer_func, regamma_lut,391391- regamma_size, has_rom);392392- if (r)393393- return r;394394- } else if (has_regamma) {395395- /* CRTC RGM goes into RGM LUT. */396396- stream->out_transfer_func->type = TF_TYPE_DISTRIBUTED_POINTS;397397- stream->out_transfer_func->tf = TRANSFER_FUNCTION_LINEAR;398398-399399- r = __set_output_tf(stream->out_transfer_func, regamma_lut,400400- regamma_size, has_rom);401401- if (r)402402- return r;403403- } else {404404- /*405405- * No CRTC RGM means we can just put the block into bypass406406- * since we don't have any plane level adjustments using it.407407- */408408- stream->out_transfer_func->type = TF_TYPE_BYPASS;409409- stream->out_transfer_func->tf = TRANSFER_FUNCTION_LINEAR;410410- }411411-412412- /*413413- * CRTC DGM goes into DGM LUT. It would be nice to place it414414- * into the RGM since it's a more featured block but we'd415415- * have to place the CTM in the OCSC in that case.416416- */417417- crtc->cm_has_degamma = has_degamma;418418-419419- /* Setup CRTC CTM. */420420- if (crtc->base.ctm) {421421- ctm = (struct drm_color_ctm *)crtc->base.ctm->data;422422-423423- /*424424- * Gamut remapping must be used for gamma correction425425- * since it comes before the regamma correction.426426- *427427- * OCSC could be used for gamma correction, but we'd need to428428- * blend the adjustments together with the required output429429- * conversion matrix - so just use the gamut remap block430430- * for now.431431- */432432- __drm_ctm_to_dc_matrix(ctm, stream->gamut_remap_matrix.matrix);433433-434434- stream->gamut_remap_matrix.enable_remap = true;435435- stream->csc_color_matrix.enable_adjustment = false;436436- } else {437437- /* Bypass CTM. */438438- stream->gamut_remap_matrix.enable_remap = false;439439- stream->csc_color_matrix.enable_adjustment = false;440440- }441441-442442- return 0;443443-}444444-445445-/**446446- * amdgpu_dm_update_plane_color_mgmt: Maps DRM color management to DC plane.447447- * @crtc: amdgpu_dm crtc state448448- * @ dc_plane_state: target DC surface449196 *450197 * Update the underlying dc_stream_state's input transfer function (ITF) in451451- * preparation for hardware commit. The transfer function used depends on452452- * the prepartion done on the stream for color management.198198+ * preparation for hardware commit. If no lut is specified by user, we default199199+ * to SRGB degamma.453200 *454454- * Returns 0 on success.201201+ * We support degamma bypass, predefined SRGB, and custom degamma202202+ *203203+ * RETURNS:204204+ * 0 on success205205+ * -EINVAL if crtc_state has a degamma_lut of invalid size206206+ * -ENOMEM if gamma allocation fails455207 */456456-int amdgpu_dm_update_plane_color_mgmt(struct dm_crtc_state *crtc,457457- struct dc_plane_state *dc_plane_state)208208+int amdgpu_dm_set_degamma_lut(struct drm_crtc_state *crtc_state,209209+ struct dc_plane_state *dc_plane_state)458210{459459- const struct drm_color_lut *degamma_lut;460460- uint32_t degamma_size;461461- int r;211211+ struct drm_property_blob *blob = crtc_state->degamma_lut;212212+ struct drm_color_lut *lut;213213+ uint32_t lut_size;214214+ struct dc_gamma *gamma;215215+ bool ret;462216463463- if (crtc->cm_has_degamma) {464464- degamma_lut = __extract_blob_lut(crtc->base.degamma_lut,465465- °amma_size);466466- ASSERT(degamma_size == MAX_COLOR_LUT_ENTRIES);467467-468468- dc_plane_state->in_transfer_func->type =469469- TF_TYPE_DISTRIBUTED_POINTS;470470-471471- /*472472- * This case isn't fully correct, but also fairly473473- * uncommon. This is userspace trying to use a474474- * legacy gamma LUT + atomic degamma LUT475475- * at the same time.476476- *477477- * Legacy gamma requires the input to be in linear478478- * space, so that means we need to apply an sRGB479479- * degamma. But color module also doesn't support480480- * a user ramp in this case so the degamma will481481- * be lost.482482- *483483- * Even if we did support it, it's still not right:484484- *485485- * Input -> CRTC DGM -> sRGB DGM -> CRTC CTM ->486486- * sRGB RGM -> CRTC RGM -> Output487487- *488488- * The CSC will be done in the wrong space since489489- * we're applying an sRGB DGM on top of the CRTC490490- * DGM.491491- *492492- * TODO: Don't use the legacy gamma interface and just493493- * map these to the atomic one instead.494494- */495495- if (crtc->cm_is_degamma_srgb)496496- dc_plane_state->in_transfer_func->tf =497497- TRANSFER_FUNCTION_SRGB;498498- else499499- dc_plane_state->in_transfer_func->tf =500500- TRANSFER_FUNCTION_LINEAR;501501-502502- r = __set_input_tf(dc_plane_state->in_transfer_func,503503- degamma_lut, degamma_size);504504- if (r)505505- return r;506506- } else if (crtc->cm_is_degamma_srgb) {507507- /*508508- * For legacy gamma support we need the regamma input509509- * in linear space. Assume that the input is sRGB.510510- */217217+ if (!blob) {218218+ /* Default to SRGB */511219 dc_plane_state->in_transfer_func->type = TF_TYPE_PREDEFINED;512220 dc_plane_state->in_transfer_func->tf = TRANSFER_FUNCTION_SRGB;513513- } else {514514- /* ...Otherwise we can just bypass the DGM block. */221221+ return 0;222222+ }223223+224224+ lut = (struct drm_color_lut *)blob->data;225225+ if (__is_lut_linear(lut, MAX_COLOR_LUT_ENTRIES)) {515226 dc_plane_state->in_transfer_func->type = TF_TYPE_BYPASS;516227 dc_plane_state->in_transfer_func->tf = TRANSFER_FUNCTION_LINEAR;228228+ return 0;229229+ }230230+231231+ gamma = dc_create_gamma();232232+ if (!gamma)233233+ return -ENOMEM;234234+235235+ lut_size = blob->length / sizeof(struct drm_color_lut);236236+ gamma->num_entries = lut_size;237237+ if (gamma->num_entries == MAX_COLOR_LUT_ENTRIES)238238+ gamma->type = GAMMA_CUSTOM;239239+ else {240240+ dc_gamma_release(&gamma);241241+ return -EINVAL;242242+ }243243+244244+ __drm_lut_to_dc_gamma(lut, gamma, false);245245+246246+ dc_plane_state->in_transfer_func->type = TF_TYPE_DISTRIBUTED_POINTS;247247+ ret = mod_color_calculate_degamma_params(dc_plane_state->in_transfer_func, gamma, true);248248+ dc_gamma_release(&gamma);249249+ if (!ret) {250250+ dc_plane_state->in_transfer_func->type = TF_TYPE_BYPASS;251251+ DRM_ERROR("Out of memory when calculating degamma params\n");252252+ return -ENOMEM;517253 }518254519255 return 0;520256}257257+