1 /* 2 * Copyright 2018 Advanced Micro Devices, Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 * OTHER DEALINGS IN THE SOFTWARE. 21 * 22 * Authors: AMD 23 * 24 */ 25 26 #include "amdgpu_mode.h" 27 #include "amdgpu_dm.h" 28 #include "dc.h" 29 #include "modules/color/color_gamma.h" 30 31 #define MAX_DRM_LUT_VALUE 0xFFFF 32 33 /* 34 * Initialize the color module. 35 * 36 * We're not using the full color module, only certain components. 37 * Only call setup functions for components that we need. 38 */ 39 void amdgpu_dm_init_color_mod(void) 40 { 41 setup_x_points_distribution(); 42 } 43 44 45 /* 46 * Return true if the given lut is a linear mapping of values, i.e. it acts 47 * like a bypass LUT. 48 * 49 * It is considered linear if the lut represents: 50 * f(a) = (0xFF00/MAX_COLOR_LUT_ENTRIES-1)a; for integer a in 51 * [0, MAX_COLOR_LUT_ENTRIES) 52 */ 53 static bool __is_lut_linear(struct drm_color_lut *lut, uint32_t size) 54 { 55 int i; 56 uint32_t expected; 57 int delta; 58 59 for (i = 0; i < size; i++) { 60 /* All color values should equal */ 61 if ((lut[i].red != lut[i].green) || (lut[i].green != lut[i].blue)) 62 return false; 63 64 expected = i * MAX_DRM_LUT_VALUE / (size-1); 65 66 /* Allow a +/-1 error. */ 67 delta = lut[i].red - expected; 68 if (delta < -1 || 1 < delta) 69 return false; 70 } 71 return true; 72 } 73 74 /** 75 * Convert the drm_color_lut to dc_gamma. The conversion depends on the size 76 * of the lut - whether or not it's legacy. 77 */ 78 static void __drm_lut_to_dc_gamma(struct drm_color_lut *lut, 79 struct dc_gamma *gamma, 80 bool is_legacy) 81 { 82 uint32_t r, g, b; 83 int i; 84 85 if (is_legacy) { 86 for (i = 0; i < MAX_COLOR_LEGACY_LUT_ENTRIES; i++) { 87 r = drm_color_lut_extract(lut[i].red, 16); 88 g = drm_color_lut_extract(lut[i].green, 16); 89 b = drm_color_lut_extract(lut[i].blue, 16); 90 91 gamma->entries.red[i] = dc_fixpt_from_int(r); 92 gamma->entries.green[i] = dc_fixpt_from_int(g); 93 gamma->entries.blue[i] = dc_fixpt_from_int(b); 94 } 95 return; 96 } 97 98 /* else */ 99 for (i = 0; i < MAX_COLOR_LUT_ENTRIES; i++) { 100 r = drm_color_lut_extract(lut[i].red, 16); 101 g = drm_color_lut_extract(lut[i].green, 16); 102 b = drm_color_lut_extract(lut[i].blue, 16); 103 104 gamma->entries.red[i] = dc_fixpt_from_fraction(r, MAX_DRM_LUT_VALUE); 105 gamma->entries.green[i] = dc_fixpt_from_fraction(g, MAX_DRM_LUT_VALUE); 106 gamma->entries.blue[i] = dc_fixpt_from_fraction(b, MAX_DRM_LUT_VALUE); 107 } 108 } 109 110 /** 111 * amdgpu_dm_set_regamma_lut: Set regamma lut for the given CRTC. 112 * @crtc: amdgpu_dm crtc state 113 * 114 * Update the underlying dc_stream_state's output transfer function (OTF) in 115 * preparation for hardware commit. If no lut is specified by user, we default 116 * to SRGB. 117 * 118 * RETURNS: 119 * 0 on success, -ENOMEM if memory cannot be allocated to calculate the OTF. 120 */ 121 int amdgpu_dm_set_regamma_lut(struct dm_crtc_state *crtc) 122 { 123 struct drm_property_blob *blob = crtc->base.gamma_lut; 124 struct dc_stream_state *stream = crtc->stream; 125 struct drm_color_lut *lut; 126 uint32_t lut_size; 127 struct dc_gamma *gamma; 128 enum dc_transfer_func_type old_type = stream->out_transfer_func->type; 129 130 bool ret; 131 132 if (!blob) { 133 /* By default, use the SRGB predefined curve.*/ 134 stream->out_transfer_func->type = TF_TYPE_PREDEFINED; 135 stream->out_transfer_func->tf = TRANSFER_FUNCTION_SRGB; 136 return 0; 137 } 138 139 lut = (struct drm_color_lut *)blob->data; 140 lut_size = blob->length / sizeof(struct drm_color_lut); 141 142 gamma = dc_create_gamma(); 143 if (!gamma) 144 return -ENOMEM; 145 146 gamma->num_entries = lut_size; 147 if (gamma->num_entries == MAX_COLOR_LEGACY_LUT_ENTRIES) 148 gamma->type = GAMMA_RGB_256; 149 else if (gamma->num_entries == MAX_COLOR_LUT_ENTRIES) 150 gamma->type = GAMMA_CS_TFM_1D; 151 else { 152 /* Invalid lut size */ 153 dc_gamma_release(&gamma); 154 return -EINVAL; 155 } 156 157 /* Convert drm_lut into dc_gamma */ 158 __drm_lut_to_dc_gamma(lut, gamma, gamma->type == GAMMA_RGB_256); 159 160 /* Call color module to translate into something DC understands. Namely 161 * a transfer function. 162 */ 163 stream->out_transfer_func->type = TF_TYPE_DISTRIBUTED_POINTS; 164 ret = mod_color_calculate_regamma_params(stream->out_transfer_func, 165 gamma, true); 166 dc_gamma_release(&gamma); 167 if (!ret) { 168 stream->out_transfer_func->type = old_type; 169 DRM_ERROR("Out of memory when calculating regamma params\n"); 170 return -ENOMEM; 171 } 172 173 return 0; 174 } 175 176 /** 177 * amdgpu_dm_set_ctm: Set the color transform matrix for the given CRTC. 178 * @crtc: amdgpu_dm crtc state 179 * 180 * Update the underlying dc_stream_state's gamut remap matrix in preparation 181 * for hardware commit. If no matrix is specified by user, gamut remap will be 182 * disabled. 183 */ 184 void amdgpu_dm_set_ctm(struct dm_crtc_state *crtc) 185 { 186 187 struct drm_property_blob *blob = crtc->base.ctm; 188 struct dc_stream_state *stream = crtc->stream; 189 struct drm_color_ctm *ctm; 190 int64_t val; 191 int i; 192 193 if (!blob) { 194 stream->gamut_remap_matrix.enable_remap = false; 195 return; 196 } 197 198 stream->gamut_remap_matrix.enable_remap = true; 199 ctm = (struct drm_color_ctm *)blob->data; 200 /* 201 * DRM gives a 3x3 matrix, but DC wants 3x4. Assuming we're operating 202 * with homogeneous coordinates, augment the matrix with 0's. 203 * 204 * The format provided is S31.32, using signed-magnitude representation. 205 * Our fixed31_32 is also S31.32, but is using 2's complement. We have 206 * to convert from signed-magnitude to 2's complement. 207 */ 208 for (i = 0; i < 12; i++) { 209 /* Skip 4th element */ 210 if (i % 4 == 3) { 211 stream->gamut_remap_matrix.matrix[i] = dc_fixpt_zero; 212 continue; 213 } 214 215 /* gamut_remap_matrix[i] = ctm[i - floor(i/4)] */ 216 val = ctm->matrix[i - (i/4)]; 217 /* If negative, convert to 2's complement. */ 218 if (val & (1ULL << 63)) 219 val = -(val & ~(1ULL << 63)); 220 221 stream->gamut_remap_matrix.matrix[i].value = val; 222 } 223 } 224 225 226 /** 227 * amdgpu_dm_set_degamma_lut: Set degamma lut for the given CRTC. 228 * @crtc: amdgpu_dm crtc state 229 * 230 * Update the underlying dc_stream_state's input transfer function (ITF) in 231 * preparation for hardware commit. If no lut is specified by user, we default 232 * to SRGB degamma. 233 * 234 * We support degamma bypass, predefined SRGB, and custom degamma 235 * 236 * RETURNS: 237 * 0 on success 238 * -EINVAL if crtc_state has a degamma_lut of invalid size 239 * -ENOMEM if gamma allocation fails 240 */ 241 int amdgpu_dm_set_degamma_lut(struct drm_crtc_state *crtc_state, 242 struct dc_plane_state *dc_plane_state) 243 { 244 struct drm_property_blob *blob = crtc_state->degamma_lut; 245 struct drm_color_lut *lut; 246 uint32_t lut_size; 247 struct dc_gamma *gamma; 248 bool ret; 249 250 if (!blob) { 251 /* Default to SRGB */ 252 dc_plane_state->in_transfer_func->type = TF_TYPE_PREDEFINED; 253 dc_plane_state->in_transfer_func->tf = TRANSFER_FUNCTION_SRGB; 254 return 0; 255 } 256 257 lut = (struct drm_color_lut *)blob->data; 258 if (__is_lut_linear(lut, MAX_COLOR_LUT_ENTRIES)) { 259 dc_plane_state->in_transfer_func->type = TF_TYPE_BYPASS; 260 dc_plane_state->in_transfer_func->tf = TRANSFER_FUNCTION_LINEAR; 261 return 0; 262 } 263 264 gamma = dc_create_gamma(); 265 if (!gamma) 266 return -ENOMEM; 267 268 lut_size = blob->length / sizeof(struct drm_color_lut); 269 gamma->num_entries = lut_size; 270 if (gamma->num_entries == MAX_COLOR_LUT_ENTRIES) 271 gamma->type = GAMMA_CUSTOM; 272 else { 273 dc_gamma_release(&gamma); 274 return -EINVAL; 275 } 276 277 __drm_lut_to_dc_gamma(lut, gamma, false); 278 279 dc_plane_state->in_transfer_func->type = TF_TYPE_DISTRIBUTED_POINTS; 280 ret = mod_color_calculate_degamma_params(dc_plane_state->in_transfer_func, gamma, true); 281 dc_gamma_release(&gamma); 282 if (!ret) { 283 dc_plane_state->in_transfer_func->type = TF_TYPE_BYPASS; 284 DRM_ERROR("Out of memory when calculating degamma params\n"); 285 return -ENOMEM; 286 } 287 288 return 0; 289 } 290 291