diff options
author | Harry Wentland <harry.wentland@amd.com> | 2017-09-12 15:58:20 -0400 |
---|---|---|
committer | Alex Deucher <alexander.deucher@amd.com> | 2017-09-26 17:01:32 -0400 |
commit | 4562236b3bc0a28aeb6ee93b2d8a849a4c4e1c7c (patch) | |
tree | 84301c04dcaaa05c3318a8fe62cf62ab52ecc162 /drivers/gpu/drm/amd/display/modules | |
parent | 9c5b2b0d409304c2e3c1f4d1c9bb4958e1d46f8f (diff) |
drm/amd/dc: Add dc display driver (v2)
Supported DCE versions: 8.0, 10.0, 11.0, 11.2
v2: rebase against 4.11
Signed-off-by: Harry Wentland <harry.wentland@amd.com>
Acked-by: Alex Deucher <alexander.deucher@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
Diffstat (limited to 'drivers/gpu/drm/amd/display/modules')
-rw-r--r-- | drivers/gpu/drm/amd/display/modules/color/color.c | 2094 | ||||
-rw-r--r-- | drivers/gpu/drm/amd/display/modules/freesync/Makefile | 10 | ||||
-rw-r--r-- | drivers/gpu/drm/amd/display/modules/freesync/freesync.c | 1158 | ||||
-rw-r--r-- | drivers/gpu/drm/amd/display/modules/inc/mod_color.h | 179 | ||||
-rw-r--r-- | drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h | 149 | ||||
-rw-r--r-- | drivers/gpu/drm/amd/display/modules/inc/mod_power.h | 112 | ||||
-rw-r--r-- | drivers/gpu/drm/amd/display/modules/power/power.c | 784 |
7 files changed, 4486 insertions, 0 deletions
diff --git a/drivers/gpu/drm/amd/display/modules/color/color.c b/drivers/gpu/drm/amd/display/modules/color/color.c new file mode 100644 index 000000000000..cf030b18f6a9 --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/color/color.c | |||
@@ -0,0 +1,2094 @@ | |||
1 | /* | ||
2 | * Copyright 2016 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 "dm_services.h" | ||
27 | #include "dc.h" | ||
28 | #include "mod_color.h" | ||
29 | #include "core_types.h" | ||
30 | #include "fixed31_32.h" | ||
31 | #include "core_dc.h" | ||
32 | |||
33 | #define MOD_COLOR_MAX_CONCURRENT_SINKS 32 | ||
34 | #define DIVIDER 10000 | ||
35 | /* S2D13 value in [-3.00...0.9999] */ | ||
36 | #define S2D13_MIN (-3 * DIVIDER) | ||
37 | #define S2D13_MAX (3 * DIVIDER) | ||
38 | #define S0D13_MIN (-1 * DIVIDER) | ||
39 | #define S0D13_MAX (1 * DIVIDER) | ||
40 | |||
41 | struct sink_caps { | ||
42 | const struct dc_sink *sink; | ||
43 | }; | ||
44 | |||
45 | struct gamut_calculation_matrix { | ||
46 | struct fixed31_32 MTransposed[9]; | ||
47 | struct fixed31_32 XYZtoRGB_Custom[9]; | ||
48 | struct fixed31_32 XYZtoRGB_Ref[9]; | ||
49 | struct fixed31_32 RGBtoXYZ_Final[9]; | ||
50 | |||
51 | struct fixed31_32 MResult[9]; | ||
52 | struct fixed31_32 fXYZofWhiteRef[9]; | ||
53 | struct fixed31_32 fXYZofRGBRef[9]; | ||
54 | }; | ||
55 | |||
56 | struct gamut_src_dst_matrix { | ||
57 | struct fixed31_32 rgbCoeffDst[9]; | ||
58 | struct fixed31_32 whiteCoeffDst[3]; | ||
59 | struct fixed31_32 rgbCoeffSrc[9]; | ||
60 | struct fixed31_32 whiteCoeffSrc[3]; | ||
61 | }; | ||
62 | |||
63 | struct color_state { | ||
64 | bool user_enable_color_temperature; | ||
65 | int custom_color_temperature; | ||
66 | struct color_space_coordinates source_gamut; | ||
67 | struct color_space_coordinates destination_gamut; | ||
68 | struct color_range contrast; | ||
69 | struct color_range saturation; | ||
70 | struct color_range brightness; | ||
71 | struct color_range hue; | ||
72 | enum dc_quantization_range preferred_quantization_range; | ||
73 | }; | ||
74 | |||
75 | struct core_color { | ||
76 | struct mod_color public; | ||
77 | struct dc *dc; | ||
78 | int num_sinks; | ||
79 | struct sink_caps *caps; | ||
80 | struct color_state *state; | ||
81 | }; | ||
82 | |||
83 | #define MOD_COLOR_TO_CORE(mod_color)\ | ||
84 | container_of(mod_color, struct core_color, public) | ||
85 | |||
86 | #define COLOR_REGISTRY_NAME "color_v1" | ||
87 | |||
88 | /*Matrix Calculation Functions*/ | ||
89 | /** | ||
90 | ***************************************************************************** | ||
91 | * Function: transposeMatrix | ||
92 | * | ||
93 | * @brief | ||
94 | * rotate the matrix 90 degrees clockwise | ||
95 | * rows become a columns and columns to rows | ||
96 | * @param [ in ] M - source matrix | ||
97 | * @param [ in ] Rows - num of Rows of the original matrix | ||
98 | * @param [ in ] Cols - num of Cols of the original matrix | ||
99 | * @param [ out] MTransposed - result matrix | ||
100 | * @return void | ||
101 | * | ||
102 | ***************************************************************************** | ||
103 | */ | ||
104 | static void transpose_matrix(const struct fixed31_32 *M, unsigned int Rows, | ||
105 | unsigned int Cols, struct fixed31_32 *MTransposed) | ||
106 | { | ||
107 | unsigned int i, j; | ||
108 | |||
109 | for (i = 0; i < Rows; i++) { | ||
110 | for (j = 0; j < Cols; j++) | ||
111 | MTransposed[(j*Rows)+i] = M[(i*Cols)+j]; | ||
112 | } | ||
113 | } | ||
114 | |||
115 | /** | ||
116 | ***************************************************************************** | ||
117 | * Function: multiplyMatrices | ||
118 | * | ||
119 | * @brief | ||
120 | * multiplies produce of two matrices: M = M1[ulRows1 x ulCols1] * | ||
121 | * M2[ulCols1 x ulCols2]. | ||
122 | * | ||
123 | * @param [ in ] M1 - first Matrix. | ||
124 | * @param [ in ] M2 - second Matrix. | ||
125 | * @param [ in ] Rows1 - num of Rows of the first Matrix | ||
126 | * @param [ in ] Cols1 - num of Cols of the first Matrix/Num of Rows | ||
127 | * of the second Matrix | ||
128 | * @param [ in ] Cols2 - num of Cols of the second Matrix | ||
129 | * @param [out ] mResult - resulting matrix. | ||
130 | * @return void | ||
131 | * | ||
132 | ***************************************************************************** | ||
133 | */ | ||
134 | static void multiply_matrices(struct fixed31_32 *mResult, | ||
135 | const struct fixed31_32 *M1, | ||
136 | const struct fixed31_32 *M2, unsigned int Rows1, | ||
137 | unsigned int Cols1, unsigned int Cols2) | ||
138 | { | ||
139 | unsigned int i, j, k; | ||
140 | |||
141 | for (i = 0; i < Rows1; i++) { | ||
142 | for (j = 0; j < Cols2; j++) { | ||
143 | mResult[(i * Cols2) + j] = dal_fixed31_32_zero; | ||
144 | for (k = 0; k < Cols1; k++) | ||
145 | mResult[(i * Cols2) + j] = | ||
146 | dal_fixed31_32_add | ||
147 | (mResult[(i * Cols2) + j], | ||
148 | dal_fixed31_32_mul(M1[(i * Cols1) + k], | ||
149 | M2[(k * Cols2) + j])); | ||
150 | } | ||
151 | } | ||
152 | } | ||
153 | |||
154 | /** | ||
155 | ***************************************************************************** | ||
156 | * Function: cFind3X3Det | ||
157 | * | ||
158 | * @brief | ||
159 | * finds determinant of given 3x3 matrix | ||
160 | * | ||
161 | * @param [ in ] m - matrix | ||
162 | * @return determinate whioch could not be zero | ||
163 | * | ||
164 | ***************************************************************************** | ||
165 | */ | ||
166 | static struct fixed31_32 find_3X3_det(const struct fixed31_32 *m) | ||
167 | { | ||
168 | struct fixed31_32 det, A1, A2, A3; | ||
169 | |||
170 | A1 = dal_fixed31_32_mul(m[0], | ||
171 | dal_fixed31_32_sub(dal_fixed31_32_mul(m[4], m[8]), | ||
172 | dal_fixed31_32_mul(m[5], m[7]))); | ||
173 | A2 = dal_fixed31_32_mul(m[1], | ||
174 | dal_fixed31_32_sub(dal_fixed31_32_mul(m[3], m[8]), | ||
175 | dal_fixed31_32_mul(m[5], m[6]))); | ||
176 | A3 = dal_fixed31_32_mul(m[2], | ||
177 | dal_fixed31_32_sub(dal_fixed31_32_mul(m[3], m[7]), | ||
178 | dal_fixed31_32_mul(m[4], m[6]))); | ||
179 | det = dal_fixed31_32_add(dal_fixed31_32_sub(A1, A2), A3); | ||
180 | return det; | ||
181 | } | ||
182 | |||
183 | |||
184 | /** | ||
185 | ***************************************************************************** | ||
186 | * Function: computeInverseMatrix_3x3 | ||
187 | * | ||
188 | * @brief | ||
189 | * builds inverse matrix | ||
190 | * | ||
191 | * @param [ in ] m - matrix | ||
192 | * @param [ out ] im - result matrix | ||
193 | * @return true if success | ||
194 | * | ||
195 | ***************************************************************************** | ||
196 | */ | ||
197 | static bool compute_inverse_matrix_3x3(const struct fixed31_32 *m, | ||
198 | struct fixed31_32 *im) | ||
199 | { | ||
200 | struct fixed31_32 determinant = find_3X3_det(m); | ||
201 | |||
202 | if (dal_fixed31_32_eq(determinant, dal_fixed31_32_zero) == false) { | ||
203 | im[0] = dal_fixed31_32_div(dal_fixed31_32_sub | ||
204 | (dal_fixed31_32_mul(m[4], m[8]), | ||
205 | dal_fixed31_32_mul(m[5], m[7])), determinant); | ||
206 | im[1] = dal_fixed31_32_neg(dal_fixed31_32_div(dal_fixed31_32_sub | ||
207 | (dal_fixed31_32_mul(m[1], m[8]), | ||
208 | dal_fixed31_32_mul(m[2], m[7])), determinant)); | ||
209 | im[2] = dal_fixed31_32_div(dal_fixed31_32_sub | ||
210 | (dal_fixed31_32_mul(m[1], m[5]), | ||
211 | dal_fixed31_32_mul(m[2], m[4])), determinant); | ||
212 | im[3] = dal_fixed31_32_neg(dal_fixed31_32_div(dal_fixed31_32_sub | ||
213 | (dal_fixed31_32_mul(m[3], m[8]), | ||
214 | dal_fixed31_32_mul(m[5], m[6])), determinant)); | ||
215 | im[4] = dal_fixed31_32_div(dal_fixed31_32_sub | ||
216 | (dal_fixed31_32_mul(m[0], m[8]), | ||
217 | dal_fixed31_32_mul(m[2], m[6])), determinant); | ||
218 | im[5] = dal_fixed31_32_neg(dal_fixed31_32_div(dal_fixed31_32_sub | ||
219 | (dal_fixed31_32_mul(m[0], m[5]), | ||
220 | dal_fixed31_32_mul(m[2], m[3])), determinant)); | ||
221 | im[6] = dal_fixed31_32_div(dal_fixed31_32_sub | ||
222 | (dal_fixed31_32_mul(m[3], m[7]), | ||
223 | dal_fixed31_32_mul(m[4], m[6])), determinant); | ||
224 | im[7] = dal_fixed31_32_neg(dal_fixed31_32_div(dal_fixed31_32_sub | ||
225 | (dal_fixed31_32_mul(m[0], m[7]), | ||
226 | dal_fixed31_32_mul(m[1], m[6])), determinant)); | ||
227 | im[8] = dal_fixed31_32_div(dal_fixed31_32_sub | ||
228 | (dal_fixed31_32_mul(m[0], m[4]), | ||
229 | dal_fixed31_32_mul(m[1], m[3])), determinant); | ||
230 | return true; | ||
231 | } | ||
232 | return false; | ||
233 | } | ||
234 | |||
235 | /** | ||
236 | ***************************************************************************** | ||
237 | * Function: calculateXYZtoRGB_M3x3 | ||
238 | * | ||
239 | * @brief | ||
240 | * Calculates transformation matrix from XYZ coordinates to RBG | ||
241 | * | ||
242 | * @param [ in ] XYZofRGB - primaries XYZ | ||
243 | * @param [ in ] XYZofWhite - white point. | ||
244 | * @param [ out ] XYZtoRGB - RGB primires | ||
245 | * @return true if success | ||
246 | * | ||
247 | ***************************************************************************** | ||
248 | */ | ||
249 | static bool calculate_XYZ_to_RGB_3x3(const struct fixed31_32 *XYZofRGB, | ||
250 | const struct fixed31_32 *XYZofWhite, | ||
251 | struct fixed31_32 *XYZtoRGB) | ||
252 | { | ||
253 | |||
254 | struct fixed31_32 MInversed[9]; | ||
255 | struct fixed31_32 SVector[3]; | ||
256 | |||
257 | /*1. Find Inverse matrix 3x3 of MTransposed*/ | ||
258 | if (!compute_inverse_matrix_3x3(XYZofRGB, MInversed)) | ||
259 | return false; | ||
260 | |||
261 | /*2. Calculate vector: |Sr Sg Sb| = [MInversed] * |Wx Wy Wz|*/ | ||
262 | multiply_matrices(SVector, MInversed, XYZofWhite, 3, 3, 1); | ||
263 | |||
264 | /*3. Calculate matrix XYZtoRGB 3x3*/ | ||
265 | XYZtoRGB[0] = dal_fixed31_32_mul(XYZofRGB[0], SVector[0]); | ||
266 | XYZtoRGB[1] = dal_fixed31_32_mul(XYZofRGB[1], SVector[1]); | ||
267 | XYZtoRGB[2] = dal_fixed31_32_mul(XYZofRGB[2], SVector[2]); | ||
268 | |||
269 | XYZtoRGB[3] = dal_fixed31_32_mul(XYZofRGB[3], SVector[0]); | ||
270 | XYZtoRGB[4] = dal_fixed31_32_mul(XYZofRGB[4], SVector[1]); | ||
271 | XYZtoRGB[5] = dal_fixed31_32_mul(XYZofRGB[5], SVector[2]); | ||
272 | |||
273 | XYZtoRGB[6] = dal_fixed31_32_mul(XYZofRGB[6], SVector[0]); | ||
274 | XYZtoRGB[7] = dal_fixed31_32_mul(XYZofRGB[7], SVector[1]); | ||
275 | XYZtoRGB[8] = dal_fixed31_32_mul(XYZofRGB[8], SVector[2]); | ||
276 | |||
277 | return true; | ||
278 | } | ||
279 | |||
280 | static bool gamut_to_color_matrix( | ||
281 | const struct fixed31_32 *pXYZofRGB,/*destination gamut*/ | ||
282 | const struct fixed31_32 *pXYZofWhite,/*destination of white point*/ | ||
283 | const struct fixed31_32 *pRefXYZofRGB,/*source gamut*/ | ||
284 | const struct fixed31_32 *pRefXYZofWhite,/*source of white point*/ | ||
285 | bool invert, | ||
286 | struct fixed31_32 *tempMatrix3X3) | ||
287 | { | ||
288 | int i = 0; | ||
289 | struct gamut_calculation_matrix *matrix = | ||
290 | dm_alloc(sizeof(struct gamut_calculation_matrix)); | ||
291 | |||
292 | struct fixed31_32 *pXYZtoRGB_Temp; | ||
293 | struct fixed31_32 *pXYZtoRGB_Final; | ||
294 | |||
295 | matrix->fXYZofWhiteRef[0] = pRefXYZofWhite[0]; | ||
296 | matrix->fXYZofWhiteRef[1] = pRefXYZofWhite[1]; | ||
297 | matrix->fXYZofWhiteRef[2] = pRefXYZofWhite[2]; | ||
298 | |||
299 | |||
300 | matrix->fXYZofRGBRef[0] = pRefXYZofRGB[0]; | ||
301 | matrix->fXYZofRGBRef[1] = pRefXYZofRGB[1]; | ||
302 | matrix->fXYZofRGBRef[2] = pRefXYZofRGB[2]; | ||
303 | |||
304 | matrix->fXYZofRGBRef[3] = pRefXYZofRGB[3]; | ||
305 | matrix->fXYZofRGBRef[4] = pRefXYZofRGB[4]; | ||
306 | matrix->fXYZofRGBRef[5] = pRefXYZofRGB[5]; | ||
307 | |||
308 | matrix->fXYZofRGBRef[6] = pRefXYZofRGB[6]; | ||
309 | matrix->fXYZofRGBRef[7] = pRefXYZofRGB[7]; | ||
310 | matrix->fXYZofRGBRef[8] = pRefXYZofRGB[8]; | ||
311 | |||
312 | /*default values - unity matrix*/ | ||
313 | while (i < 9) { | ||
314 | if (i == 0 || i == 4 || i == 8) | ||
315 | tempMatrix3X3[i] = dal_fixed31_32_one; | ||
316 | else | ||
317 | tempMatrix3X3[i] = dal_fixed31_32_zero; | ||
318 | i++; | ||
319 | } | ||
320 | |||
321 | /*1. Decide about the order of calculation. | ||
322 | * bInvert == FALSE --> RGBtoXYZ_Ref * XYZtoRGB_Custom | ||
323 | * bInvert == TRUE --> RGBtoXYZ_Custom * XYZtoRGB_Ref */ | ||
324 | if (invert) { | ||
325 | pXYZtoRGB_Temp = matrix->XYZtoRGB_Custom; | ||
326 | pXYZtoRGB_Final = matrix->XYZtoRGB_Ref; | ||
327 | } else { | ||
328 | pXYZtoRGB_Temp = matrix->XYZtoRGB_Ref; | ||
329 | pXYZtoRGB_Final = matrix->XYZtoRGB_Custom; | ||
330 | } | ||
331 | |||
332 | /*2. Calculate XYZtoRGB_Ref*/ | ||
333 | transpose_matrix(matrix->fXYZofRGBRef, 3, 3, matrix->MTransposed); | ||
334 | |||
335 | if (!calculate_XYZ_to_RGB_3x3( | ||
336 | matrix->MTransposed, | ||
337 | matrix->fXYZofWhiteRef, | ||
338 | matrix->XYZtoRGB_Ref)) | ||
339 | goto function_fail; | ||
340 | |||
341 | /*3. Calculate XYZtoRGB_Custom*/ | ||
342 | transpose_matrix(pXYZofRGB, 3, 3, matrix->MTransposed); | ||
343 | |||
344 | if (!calculate_XYZ_to_RGB_3x3( | ||
345 | matrix->MTransposed, | ||
346 | pXYZofWhite, | ||
347 | matrix->XYZtoRGB_Custom)) | ||
348 | goto function_fail; | ||
349 | |||
350 | /*4. Calculate RGBtoXYZ - | ||
351 | * inverse matrix 3x3 of XYZtoRGB_Ref or XYZtoRGB_Custom*/ | ||
352 | if (!compute_inverse_matrix_3x3(pXYZtoRGB_Temp, matrix->RGBtoXYZ_Final)) | ||
353 | goto function_fail; | ||
354 | |||
355 | /*5. Calculate M(3x3) = RGBtoXYZ * XYZtoRGB*/ | ||
356 | multiply_matrices(matrix->MResult, matrix->RGBtoXYZ_Final, | ||
357 | pXYZtoRGB_Final, 3, 3, 3); | ||
358 | |||
359 | for (i = 0; i < 9; i++) | ||
360 | tempMatrix3X3[i] = matrix->MResult[i]; | ||
361 | |||
362 | dm_free(matrix); | ||
363 | |||
364 | return true; | ||
365 | |||
366 | function_fail: | ||
367 | dm_free(matrix); | ||
368 | return false; | ||
369 | } | ||
370 | |||
371 | static bool build_gamut_remap_matrix | ||
372 | (struct color_space_coordinates gamut_description, | ||
373 | struct fixed31_32 *rgb_matrix, | ||
374 | struct fixed31_32 *white_point_matrix) | ||
375 | { | ||
376 | struct fixed31_32 fixed_blueX = dal_fixed31_32_from_fraction | ||
377 | (gamut_description.blueX, DIVIDER); | ||
378 | struct fixed31_32 fixed_blueY = dal_fixed31_32_from_fraction | ||
379 | (gamut_description.blueY, DIVIDER); | ||
380 | struct fixed31_32 fixed_greenX = dal_fixed31_32_from_fraction | ||
381 | (gamut_description.greenX, DIVIDER); | ||
382 | struct fixed31_32 fixed_greenY = dal_fixed31_32_from_fraction | ||
383 | (gamut_description.greenY, DIVIDER); | ||
384 | struct fixed31_32 fixed_redX = dal_fixed31_32_from_fraction | ||
385 | (gamut_description.redX, DIVIDER); | ||
386 | struct fixed31_32 fixed_redY = dal_fixed31_32_from_fraction | ||
387 | (gamut_description.redY, DIVIDER); | ||
388 | struct fixed31_32 fixed_whiteX = dal_fixed31_32_from_fraction | ||
389 | (gamut_description.whiteX, DIVIDER); | ||
390 | struct fixed31_32 fixed_whiteY = dal_fixed31_32_from_fraction | ||
391 | (gamut_description.whiteY, DIVIDER); | ||
392 | |||
393 | rgb_matrix[0] = dal_fixed31_32_div(fixed_redX, fixed_redY); | ||
394 | rgb_matrix[1] = dal_fixed31_32_one; | ||
395 | rgb_matrix[2] = dal_fixed31_32_div(dal_fixed31_32_sub | ||
396 | (dal_fixed31_32_sub(dal_fixed31_32_one, fixed_redX), | ||
397 | fixed_redY), fixed_redY); | ||
398 | |||
399 | rgb_matrix[3] = dal_fixed31_32_div(fixed_greenX, fixed_greenY); | ||
400 | rgb_matrix[4] = dal_fixed31_32_one; | ||
401 | rgb_matrix[5] = dal_fixed31_32_div(dal_fixed31_32_sub | ||
402 | (dal_fixed31_32_sub(dal_fixed31_32_one, fixed_greenX), | ||
403 | fixed_greenY), fixed_greenY); | ||
404 | |||
405 | rgb_matrix[6] = dal_fixed31_32_div(fixed_blueX, fixed_blueY); | ||
406 | rgb_matrix[7] = dal_fixed31_32_one; | ||
407 | rgb_matrix[8] = dal_fixed31_32_div(dal_fixed31_32_sub | ||
408 | (dal_fixed31_32_sub(dal_fixed31_32_one, fixed_blueX), | ||
409 | fixed_blueY), fixed_blueY); | ||
410 | |||
411 | white_point_matrix[0] = dal_fixed31_32_div(fixed_whiteX, fixed_whiteY); | ||
412 | white_point_matrix[1] = dal_fixed31_32_one; | ||
413 | white_point_matrix[2] = dal_fixed31_32_div(dal_fixed31_32_sub | ||
414 | (dal_fixed31_32_sub(dal_fixed31_32_one, fixed_whiteX), | ||
415 | fixed_whiteY), fixed_whiteY); | ||
416 | |||
417 | return true; | ||
418 | } | ||
419 | |||
420 | static bool check_dc_support(const struct dc *dc) | ||
421 | { | ||
422 | if (dc->stream_funcs.set_gamut_remap == NULL) | ||
423 | return false; | ||
424 | |||
425 | return true; | ||
426 | } | ||
427 | |||
428 | static uint16_t fixed_point_to_int_frac( | ||
429 | struct fixed31_32 arg, | ||
430 | uint8_t integer_bits, | ||
431 | uint8_t fractional_bits) | ||
432 | { | ||
433 | int32_t numerator; | ||
434 | int32_t divisor = 1 << fractional_bits; | ||
435 | |||
436 | uint16_t result; | ||
437 | |||
438 | uint16_t d = (uint16_t)dal_fixed31_32_floor( | ||
439 | dal_fixed31_32_abs( | ||
440 | arg)); | ||
441 | |||
442 | if (d <= (uint16_t)(1 << integer_bits) - (1 / (uint16_t)divisor)) | ||
443 | numerator = (uint16_t)dal_fixed31_32_floor( | ||
444 | dal_fixed31_32_mul_int( | ||
445 | arg, | ||
446 | divisor)); | ||
447 | else { | ||
448 | numerator = dal_fixed31_32_floor( | ||
449 | dal_fixed31_32_sub( | ||
450 | dal_fixed31_32_from_int( | ||
451 | 1LL << integer_bits), | ||
452 | dal_fixed31_32_recip( | ||
453 | dal_fixed31_32_from_int( | ||
454 | divisor)))); | ||
455 | } | ||
456 | |||
457 | if (numerator >= 0) | ||
458 | result = (uint16_t)numerator; | ||
459 | else | ||
460 | result = (uint16_t)( | ||
461 | (1 << (integer_bits + fractional_bits + 1)) + numerator); | ||
462 | |||
463 | if ((result != 0) && dal_fixed31_32_lt( | ||
464 | arg, dal_fixed31_32_zero)) | ||
465 | result |= 1 << (integer_bits + fractional_bits); | ||
466 | |||
467 | return result; | ||
468 | } | ||
469 | |||
470 | /** | ||
471 | * convert_float_matrix | ||
472 | * This converts a double into HW register spec defined format S2D13. | ||
473 | * @param : | ||
474 | * @return None | ||
475 | */ | ||
476 | |||
477 | static void convert_float_matrix_legacy( | ||
478 | uint16_t *matrix, | ||
479 | struct fixed31_32 *flt, | ||
480 | uint32_t buffer_size) | ||
481 | { | ||
482 | const struct fixed31_32 min_2_13 = | ||
483 | dal_fixed31_32_from_fraction(S2D13_MIN, DIVIDER); | ||
484 | const struct fixed31_32 max_2_13 = | ||
485 | dal_fixed31_32_from_fraction(S2D13_MAX, DIVIDER); | ||
486 | uint32_t i; | ||
487 | |||
488 | for (i = 0; i < buffer_size; ++i) { | ||
489 | uint32_t reg_value = | ||
490 | fixed_point_to_int_frac( | ||
491 | dal_fixed31_32_clamp( | ||
492 | flt[i], | ||
493 | min_2_13, | ||
494 | max_2_13), | ||
495 | 2, | ||
496 | 13); | ||
497 | |||
498 | matrix[i] = (uint16_t)reg_value; | ||
499 | } | ||
500 | } | ||
501 | |||
502 | static void convert_float_matrix( | ||
503 | uint16_t *matrix, | ||
504 | struct fixed31_32 *flt, | ||
505 | uint32_t buffer_size) | ||
506 | { | ||
507 | const struct fixed31_32 min_0_13 = | ||
508 | dal_fixed31_32_from_fraction(S0D13_MIN, DIVIDER); | ||
509 | const struct fixed31_32 max_0_13 = | ||
510 | dal_fixed31_32_from_fraction(S0D13_MAX, DIVIDER); | ||
511 | const struct fixed31_32 min_2_13 = | ||
512 | dal_fixed31_32_from_fraction(S2D13_MIN, DIVIDER); | ||
513 | const struct fixed31_32 max_2_13 = | ||
514 | dal_fixed31_32_from_fraction(S2D13_MAX, DIVIDER); | ||
515 | uint32_t i; | ||
516 | uint16_t temp_matrix[12]; | ||
517 | |||
518 | for (i = 0; i < buffer_size; ++i) { | ||
519 | if (i == 3 || i == 7 || i == 11) { | ||
520 | uint32_t reg_value = | ||
521 | fixed_point_to_int_frac( | ||
522 | dal_fixed31_32_clamp( | ||
523 | flt[i], | ||
524 | min_0_13, | ||
525 | max_0_13), | ||
526 | 2, | ||
527 | 13); | ||
528 | |||
529 | temp_matrix[i] = (uint16_t)reg_value; | ||
530 | } else { | ||
531 | uint32_t reg_value = | ||
532 | fixed_point_to_int_frac( | ||
533 | dal_fixed31_32_clamp( | ||
534 | flt[i], | ||
535 | min_2_13, | ||
536 | max_2_13), | ||
537 | 2, | ||
538 | 13); | ||
539 | |||
540 | temp_matrix[i] = (uint16_t)reg_value; | ||
541 | } | ||
542 | } | ||
543 | |||
544 | matrix[4] = temp_matrix[0]; | ||
545 | matrix[5] = temp_matrix[1]; | ||
546 | matrix[6] = temp_matrix[2]; | ||
547 | matrix[7] = temp_matrix[3]; | ||
548 | |||
549 | matrix[8] = temp_matrix[4]; | ||
550 | matrix[9] = temp_matrix[5]; | ||
551 | matrix[10] = temp_matrix[6]; | ||
552 | matrix[11] = temp_matrix[7]; | ||
553 | |||
554 | matrix[0] = temp_matrix[8]; | ||
555 | matrix[1] = temp_matrix[9]; | ||
556 | matrix[2] = temp_matrix[10]; | ||
557 | matrix[3] = temp_matrix[11]; | ||
558 | } | ||
559 | |||
560 | static int get_hw_value_from_sw_value(int swVal, int swMin, | ||
561 | int swMax, int hwMin, int hwMax) | ||
562 | { | ||
563 | int dSW = swMax - swMin; /*software adjustment range size*/ | ||
564 | int dHW = hwMax - hwMin; /*hardware adjustment range size*/ | ||
565 | int hwVal; /*HW adjustment value*/ | ||
566 | |||
567 | /* error case, I preserve the behavior from the predecessor | ||
568 | *getHwStepFromSwHwMinMaxValue (removed in Feb 2013) | ||
569 | *which was the FP version that only computed SCLF (i.e. dHW/dSW). | ||
570 | *it would return 0 in this case so | ||
571 | *hwVal = hwMin from the formula given in @brief | ||
572 | */ | ||
573 | if (dSW == 0) | ||
574 | return hwMin; | ||
575 | |||
576 | /*it's quite often that ranges match, | ||
577 | *e.g. for overlay colors currently (Feb 2013) | ||
578 | *only brightness has a different | ||
579 | *HW range, and in this case no multiplication or division is needed, | ||
580 | *and if minimums match, no calculation at all | ||
581 | */ | ||
582 | if (dSW != dHW) { | ||
583 | hwVal = (swVal - swMin)*dHW/dSW + hwMin; | ||
584 | } else { | ||
585 | hwVal = swVal; | ||
586 | if (swMin != hwMin) | ||
587 | hwVal += (hwMin - swMin); | ||
588 | } | ||
589 | |||
590 | return hwVal; | ||
591 | } | ||
592 | |||
593 | static void initialize_fix_point_color_values( | ||
594 | struct core_color *core_color, | ||
595 | unsigned int sink_index, | ||
596 | struct fixed31_32 *grph_cont, | ||
597 | struct fixed31_32 *grph_sat, | ||
598 | struct fixed31_32 *grph_bright, | ||
599 | struct fixed31_32 *sin_grph_hue, | ||
600 | struct fixed31_32 *cos_grph_hue) | ||
601 | { | ||
602 | /* Hue adjustment could be negative. -45 ~ +45 */ | ||
603 | struct fixed31_32 hue = | ||
604 | dal_fixed31_32_mul( | ||
605 | dal_fixed31_32_from_fraction | ||
606 | (get_hw_value_from_sw_value | ||
607 | (core_color->state[sink_index].hue.current, | ||
608 | core_color->state[sink_index].hue.min, | ||
609 | core_color->state[sink_index].hue.max, | ||
610 | -30, 30), 180), | ||
611 | dal_fixed31_32_pi); | ||
612 | |||
613 | *sin_grph_hue = dal_fixed31_32_sin(hue); | ||
614 | *cos_grph_hue = dal_fixed31_32_cos(hue); | ||
615 | |||
616 | *grph_cont = | ||
617 | dal_fixed31_32_from_fraction(get_hw_value_from_sw_value | ||
618 | (core_color->state[sink_index].contrast.current, | ||
619 | core_color->state[sink_index].contrast.min, | ||
620 | core_color->state[sink_index].contrast.max, | ||
621 | 50, 150), 100); | ||
622 | *grph_sat = | ||
623 | dal_fixed31_32_from_fraction(get_hw_value_from_sw_value | ||
624 | (core_color->state[sink_index].saturation.current, | ||
625 | core_color->state[sink_index].saturation.min, | ||
626 | core_color->state[sink_index].saturation.max, | ||
627 | 0, 200), 100); | ||
628 | *grph_bright = | ||
629 | dal_fixed31_32_from_fraction(get_hw_value_from_sw_value | ||
630 | (core_color->state[sink_index].brightness.current, | ||
631 | core_color->state[sink_index].brightness.min, | ||
632 | core_color->state[sink_index].brightness.max, | ||
633 | -25, 25), 100); | ||
634 | } | ||
635 | |||
636 | |||
637 | /* Given a specific dc_sink* this function finds its equivalent | ||
638 | * on the dc_sink array and returns the corresponding index | ||
639 | */ | ||
640 | static unsigned int sink_index_from_sink(struct core_color *core_color, | ||
641 | const struct dc_sink *sink) | ||
642 | { | ||
643 | unsigned int index = 0; | ||
644 | |||
645 | for (index = 0; index < core_color->num_sinks; index++) | ||
646 | if (core_color->caps[index].sink == sink) | ||
647 | return index; | ||
648 | |||
649 | /* Could not find sink requested */ | ||
650 | ASSERT(false); | ||
651 | return index; | ||
652 | } | ||
653 | |||
654 | static void calculate_rgb_matrix_legacy(struct core_color *core_color, | ||
655 | unsigned int sink_index, | ||
656 | struct fixed31_32 *rgb_matrix) | ||
657 | { | ||
658 | const struct fixed31_32 k1 = | ||
659 | dal_fixed31_32_from_fraction(701000, 1000000); | ||
660 | const struct fixed31_32 k2 = | ||
661 | dal_fixed31_32_from_fraction(236568, 1000000); | ||
662 | const struct fixed31_32 k3 = | ||
663 | dal_fixed31_32_from_fraction(-587000, 1000000); | ||
664 | const struct fixed31_32 k4 = | ||
665 | dal_fixed31_32_from_fraction(464432, 1000000); | ||
666 | const struct fixed31_32 k5 = | ||
667 | dal_fixed31_32_from_fraction(-114000, 1000000); | ||
668 | const struct fixed31_32 k6 = | ||
669 | dal_fixed31_32_from_fraction(-701000, 1000000); | ||
670 | const struct fixed31_32 k7 = | ||
671 | dal_fixed31_32_from_fraction(-299000, 1000000); | ||
672 | const struct fixed31_32 k8 = | ||
673 | dal_fixed31_32_from_fraction(-292569, 1000000); | ||
674 | const struct fixed31_32 k9 = | ||
675 | dal_fixed31_32_from_fraction(413000, 1000000); | ||
676 | const struct fixed31_32 k10 = | ||
677 | dal_fixed31_32_from_fraction(-92482, 1000000); | ||
678 | const struct fixed31_32 k11 = | ||
679 | dal_fixed31_32_from_fraction(-114000, 1000000); | ||
680 | const struct fixed31_32 k12 = | ||
681 | dal_fixed31_32_from_fraction(385051, 1000000); | ||
682 | const struct fixed31_32 k13 = | ||
683 | dal_fixed31_32_from_fraction(-299000, 1000000); | ||
684 | const struct fixed31_32 k14 = | ||
685 | dal_fixed31_32_from_fraction(886000, 1000000); | ||
686 | const struct fixed31_32 k15 = | ||
687 | dal_fixed31_32_from_fraction(-587000, 1000000); | ||
688 | const struct fixed31_32 k16 = | ||
689 | dal_fixed31_32_from_fraction(-741914, 1000000); | ||
690 | const struct fixed31_32 k17 = | ||
691 | dal_fixed31_32_from_fraction(886000, 1000000); | ||
692 | const struct fixed31_32 k18 = | ||
693 | dal_fixed31_32_from_fraction(-144086, 1000000); | ||
694 | |||
695 | const struct fixed31_32 luma_r = | ||
696 | dal_fixed31_32_from_fraction(299, 1000); | ||
697 | const struct fixed31_32 luma_g = | ||
698 | dal_fixed31_32_from_fraction(587, 1000); | ||
699 | const struct fixed31_32 luma_b = | ||
700 | dal_fixed31_32_from_fraction(114, 1000); | ||
701 | |||
702 | struct fixed31_32 grph_cont; | ||
703 | struct fixed31_32 grph_sat; | ||
704 | struct fixed31_32 grph_bright; | ||
705 | struct fixed31_32 sin_grph_hue; | ||
706 | struct fixed31_32 cos_grph_hue; | ||
707 | |||
708 | initialize_fix_point_color_values( | ||
709 | core_color, sink_index, &grph_cont, &grph_sat, | ||
710 | &grph_bright, &sin_grph_hue, &cos_grph_hue); | ||
711 | |||
712 | /* COEF_1_1 = GrphCont * (LumaR + GrphSat * (Cos(GrphHue) * K1 +*/ | ||
713 | /* Sin(GrphHue) * K2))*/ | ||
714 | /* (Cos(GrphHue) * K1 + Sin(GrphHue) * K2)*/ | ||
715 | rgb_matrix[0] = | ||
716 | dal_fixed31_32_add( | ||
717 | dal_fixed31_32_mul(cos_grph_hue, k1), | ||
718 | dal_fixed31_32_mul(sin_grph_hue, k2)); | ||
719 | /* GrphSat * (Cos(GrphHue) * K1 + Sin(GrphHue) * K2 */ | ||
720 | rgb_matrix[0] = dal_fixed31_32_mul(grph_sat, rgb_matrix[0]); | ||
721 | /* (LumaR + GrphSat * (Cos(GrphHue) * K1 + Sin(GrphHue) * K2))*/ | ||
722 | rgb_matrix[0] = dal_fixed31_32_add(luma_r, rgb_matrix[0]); | ||
723 | /* GrphCont * (LumaR + GrphSat * (Cos(GrphHue) * K1 + Sin(GrphHue)**/ | ||
724 | /* K2))*/ | ||
725 | rgb_matrix[0] = dal_fixed31_32_mul(grph_cont, rgb_matrix[0]); | ||
726 | |||
727 | /* COEF_1_2 = GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K3 +*/ | ||
728 | /* Sin(GrphHue) * K4))*/ | ||
729 | /* (Cos(GrphHue) * K3 + Sin(GrphHue) * K4)*/ | ||
730 | rgb_matrix[1] = | ||
731 | dal_fixed31_32_add( | ||
732 | dal_fixed31_32_mul(cos_grph_hue, k3), | ||
733 | dal_fixed31_32_mul(sin_grph_hue, k4)); | ||
734 | /* GrphSat * (Cos(GrphHue) * K3 + Sin(GrphHue) * K4)*/ | ||
735 | rgb_matrix[1] = dal_fixed31_32_mul(grph_sat, rgb_matrix[1]); | ||
736 | /* (LumaG + GrphSat * (Cos(GrphHue) * K3 + Sin(GrphHue) * K4))*/ | ||
737 | rgb_matrix[1] = dal_fixed31_32_add(luma_g, rgb_matrix[1]); | ||
738 | /* GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K3 + Sin(GrphHue)**/ | ||
739 | /* K4))*/ | ||
740 | rgb_matrix[1] = dal_fixed31_32_mul(grph_cont, rgb_matrix[1]); | ||
741 | |||
742 | /* COEF_1_3 = GrphCont * (LumaB + GrphSat * (Cos(GrphHue) * K5 +*/ | ||
743 | /* Sin(GrphHue) * K6))*/ | ||
744 | /* (Cos(GrphHue) * K5 + Sin(GrphHue) * K6)*/ | ||
745 | rgb_matrix[2] = | ||
746 | dal_fixed31_32_add( | ||
747 | dal_fixed31_32_mul(cos_grph_hue, k5), | ||
748 | dal_fixed31_32_mul(sin_grph_hue, k6)); | ||
749 | /* GrphSat * (Cos(GrphHue) * K5 + Sin(GrphHue) * K6)*/ | ||
750 | rgb_matrix[2] = dal_fixed31_32_mul(grph_sat, rgb_matrix[2]); | ||
751 | /* LumaB + GrphSat * (Cos(GrphHue) * K5 + Sin(GrphHue) * K6)*/ | ||
752 | rgb_matrix[2] = dal_fixed31_32_add(luma_b, rgb_matrix[2]); | ||
753 | /* GrphCont * (LumaB + GrphSat * (Cos(GrphHue) * K5 + Sin(GrphHue)**/ | ||
754 | /* K6))*/ | ||
755 | rgb_matrix[2] = dal_fixed31_32_mul(grph_cont, rgb_matrix[2]); | ||
756 | |||
757 | /* COEF_1_4 = GrphBright*/ | ||
758 | rgb_matrix[3] = grph_bright; | ||
759 | |||
760 | /* COEF_2_1 = GrphCont * (LumaR + GrphSat * (Cos(GrphHue) * K7 +*/ | ||
761 | /* Sin(GrphHue) * K8))*/ | ||
762 | /* (Cos(GrphHue) * K7 + Sin(GrphHue) * K8)*/ | ||
763 | rgb_matrix[4] = | ||
764 | dal_fixed31_32_add( | ||
765 | dal_fixed31_32_mul(cos_grph_hue, k7), | ||
766 | dal_fixed31_32_mul(sin_grph_hue, k8)); | ||
767 | /* GrphSat * (Cos(GrphHue) * K7 + Sin(GrphHue) * K8)*/ | ||
768 | rgb_matrix[4] = dal_fixed31_32_mul(grph_sat, rgb_matrix[4]); | ||
769 | /* (LumaR + GrphSat * (Cos(GrphHue) * K7 + Sin(GrphHue) * K8))*/ | ||
770 | rgb_matrix[4] = dal_fixed31_32_add(luma_r, rgb_matrix[4]); | ||
771 | /* GrphCont * (LumaR + GrphSat * (Cos(GrphHue) * K7 + Sin(GrphHue)**/ | ||
772 | /* K8))*/ | ||
773 | rgb_matrix[4] = dal_fixed31_32_mul(grph_cont, rgb_matrix[4]); | ||
774 | |||
775 | /* COEF_2_2 = GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K9 +*/ | ||
776 | /* Sin(GrphHue) * K10))*/ | ||
777 | /* (Cos(GrphHue) * K9 + Sin(GrphHue) * K10))*/ | ||
778 | rgb_matrix[5] = | ||
779 | dal_fixed31_32_add( | ||
780 | dal_fixed31_32_mul(cos_grph_hue, k9), | ||
781 | dal_fixed31_32_mul(sin_grph_hue, k10)); | ||
782 | /* GrphSat * (Cos(GrphHue) * K9 + Sin(GrphHue) * K10))*/ | ||
783 | rgb_matrix[5] = dal_fixed31_32_mul(grph_sat, rgb_matrix[5]); | ||
784 | /* (LumaG + GrphSat * (Cos(GrphHue) * K9 + Sin(GrphHue) * K10))*/ | ||
785 | rgb_matrix[5] = dal_fixed31_32_add(luma_g, rgb_matrix[5]); | ||
786 | /* GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K9 + Sin(GrphHue)**/ | ||
787 | /* K10))*/ | ||
788 | rgb_matrix[5] = dal_fixed31_32_mul(grph_cont, rgb_matrix[5]); | ||
789 | |||
790 | /* COEF_2_3 = GrphCont * (LumaB + GrphSat * (Cos(GrphHue) * K11 +*/ | ||
791 | /* Sin(GrphHue) * K12))*/ | ||
792 | /* (Cos(GrphHue) * K11 + Sin(GrphHue) * K12))*/ | ||
793 | rgb_matrix[6] = | ||
794 | dal_fixed31_32_add( | ||
795 | dal_fixed31_32_mul(cos_grph_hue, k11), | ||
796 | dal_fixed31_32_mul(sin_grph_hue, k12)); | ||
797 | /* GrphSat * (Cos(GrphHue) * K11 + Sin(GrphHue) * K12))*/ | ||
798 | rgb_matrix[6] = dal_fixed31_32_mul(grph_sat, rgb_matrix[6]); | ||
799 | /* (LumaB + GrphSat * (Cos(GrphHue) * K11 + Sin(GrphHue) * K12))*/ | ||
800 | rgb_matrix[6] = dal_fixed31_32_add(luma_b, rgb_matrix[6]); | ||
801 | /* GrphCont * (LumaB + GrphSat * (Cos(GrphHue) * K11 + Sin(GrphHue)**/ | ||
802 | /* K12))*/ | ||
803 | rgb_matrix[6] = dal_fixed31_32_mul(grph_cont, rgb_matrix[6]); | ||
804 | |||
805 | /* COEF_2_4 = GrphBright*/ | ||
806 | rgb_matrix[7] = grph_bright; | ||
807 | |||
808 | /* COEF_3_1 = GrphCont * (LumaR + GrphSat * (Cos(GrphHue) * K13 +*/ | ||
809 | /* Sin(GrphHue) * K14))*/ | ||
810 | /* (Cos(GrphHue) * K13 + Sin(GrphHue) * K14)) */ | ||
811 | rgb_matrix[8] = | ||
812 | dal_fixed31_32_add( | ||
813 | dal_fixed31_32_mul(cos_grph_hue, k13), | ||
814 | dal_fixed31_32_mul(sin_grph_hue, k14)); | ||
815 | /* GrphSat * (Cos(GrphHue) * K13 + Sin(GrphHue) * K14)) */ | ||
816 | rgb_matrix[8] = dal_fixed31_32_mul(grph_sat, rgb_matrix[8]); | ||
817 | /* (LumaR + GrphSat * (Cos(GrphHue) * K13 + Sin(GrphHue) * K14)) */ | ||
818 | rgb_matrix[8] = dal_fixed31_32_add(luma_r, rgb_matrix[8]); | ||
819 | /* GrphCont * (LumaR + GrphSat * (Cos(GrphHue) * K13 + Sin(GrphHue)**/ | ||
820 | /* K14)) */ | ||
821 | rgb_matrix[8] = dal_fixed31_32_mul(grph_cont, rgb_matrix[8]); | ||
822 | |||
823 | /* COEF_3_2 = GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K15 +*/ | ||
824 | /* Sin(GrphHue) * K16)) */ | ||
825 | /* GrphSat * (Cos(GrphHue) * K15 + Sin(GrphHue) * K16) */ | ||
826 | rgb_matrix[9] = | ||
827 | dal_fixed31_32_add( | ||
828 | dal_fixed31_32_mul(cos_grph_hue, k15), | ||
829 | dal_fixed31_32_mul(sin_grph_hue, k16)); | ||
830 | /* (LumaG + GrphSat * (Cos(GrphHue) * K15 + Sin(GrphHue) * K16)) */ | ||
831 | rgb_matrix[9] = dal_fixed31_32_mul(grph_sat, rgb_matrix[9]); | ||
832 | /* (LumaG + GrphSat * (Cos(GrphHue) * K15 + Sin(GrphHue) * K16)) */ | ||
833 | rgb_matrix[9] = dal_fixed31_32_add(luma_g, rgb_matrix[9]); | ||
834 | /* GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K15 + Sin(GrphHue)**/ | ||
835 | /* K16)) */ | ||
836 | rgb_matrix[9] = dal_fixed31_32_mul(grph_cont, rgb_matrix[9]); | ||
837 | |||
838 | /* COEF_3_3 = GrphCont * (LumaB + GrphSat * (Cos(GrphHue) * K17 +*/ | ||
839 | /* Sin(GrphHue) * K18)) */ | ||
840 | /* (Cos(GrphHue) * K17 + Sin(GrphHue) * K18)) */ | ||
841 | rgb_matrix[10] = | ||
842 | dal_fixed31_32_add( | ||
843 | dal_fixed31_32_mul(cos_grph_hue, k17), | ||
844 | dal_fixed31_32_mul(sin_grph_hue, k18)); | ||
845 | /* GrphSat * (Cos(GrphHue) * K17 + Sin(GrphHue) * K18)) */ | ||
846 | rgb_matrix[10] = dal_fixed31_32_mul(grph_sat, rgb_matrix[10]); | ||
847 | /* (LumaB + GrphSat * (Cos(GrphHue) * K17 + Sin(GrphHue) * K18)) */ | ||
848 | rgb_matrix[10] = dal_fixed31_32_add(luma_b, rgb_matrix[10]); | ||
849 | /* GrphCont * (LumaB + GrphSat * (Cos(GrphHue) * K17 + Sin(GrphHue)**/ | ||
850 | /* K18)) */ | ||
851 | rgb_matrix[10] = dal_fixed31_32_mul(grph_cont, rgb_matrix[10]); | ||
852 | |||
853 | /* COEF_3_4 = GrphBright */ | ||
854 | rgb_matrix[11] = grph_bright; | ||
855 | } | ||
856 | |||
857 | static void calculate_rgb_limited_range_matrix(struct core_color *core_color, | ||
858 | unsigned int sink_index, struct fixed31_32 *rgb_matrix) | ||
859 | { | ||
860 | struct fixed31_32 ideal[12]; | ||
861 | |||
862 | static const int32_t matrix_[] = { | ||
863 | 85546875, 0, 0, 6250000, | ||
864 | 0, 85546875, 0, 6250000, | ||
865 | 0, 0, 85546875, 6250000 | ||
866 | }; | ||
867 | |||
868 | uint32_t i = 0; | ||
869 | |||
870 | do { | ||
871 | ideal[i] = dal_fixed31_32_from_fraction( | ||
872 | matrix_[i], | ||
873 | 100000000); | ||
874 | ++i; | ||
875 | } while (i != ARRAY_SIZE(matrix_)); | ||
876 | |||
877 | |||
878 | struct fixed31_32 grph_cont; | ||
879 | struct fixed31_32 grph_sat; | ||
880 | struct fixed31_32 grph_bright; | ||
881 | struct fixed31_32 sin_grph_hue; | ||
882 | struct fixed31_32 cos_grph_hue; | ||
883 | |||
884 | initialize_fix_point_color_values( | ||
885 | core_color, sink_index, &grph_cont, &grph_sat, | ||
886 | &grph_bright, &sin_grph_hue, &cos_grph_hue); | ||
887 | |||
888 | const struct fixed31_32 multiplier = | ||
889 | dal_fixed31_32_mul(grph_cont, grph_sat); | ||
890 | |||
891 | rgb_matrix[8] = dal_fixed31_32_mul(ideal[0], grph_cont); | ||
892 | |||
893 | rgb_matrix[9] = dal_fixed31_32_mul(ideal[1], grph_cont); | ||
894 | |||
895 | rgb_matrix[10] = dal_fixed31_32_mul(ideal[2], grph_cont); | ||
896 | |||
897 | rgb_matrix[11] = dal_fixed31_32_add( | ||
898 | ideal[3], | ||
899 | dal_fixed31_32_mul( | ||
900 | grph_bright, | ||
901 | dal_fixed31_32_from_fraction(86, 100))); | ||
902 | |||
903 | rgb_matrix[0] = dal_fixed31_32_mul( | ||
904 | multiplier, | ||
905 | dal_fixed31_32_add( | ||
906 | dal_fixed31_32_mul( | ||
907 | ideal[8], | ||
908 | sin_grph_hue), | ||
909 | dal_fixed31_32_mul( | ||
910 | ideal[4], | ||
911 | cos_grph_hue))); | ||
912 | |||
913 | rgb_matrix[1] = dal_fixed31_32_mul( | ||
914 | multiplier, | ||
915 | dal_fixed31_32_add( | ||
916 | dal_fixed31_32_mul( | ||
917 | ideal[9], | ||
918 | sin_grph_hue), | ||
919 | dal_fixed31_32_mul( | ||
920 | ideal[5], | ||
921 | cos_grph_hue))); | ||
922 | |||
923 | rgb_matrix[2] = dal_fixed31_32_mul( | ||
924 | multiplier, | ||
925 | dal_fixed31_32_add( | ||
926 | dal_fixed31_32_mul( | ||
927 | ideal[10], | ||
928 | sin_grph_hue), | ||
929 | dal_fixed31_32_mul( | ||
930 | ideal[6], | ||
931 | cos_grph_hue))); | ||
932 | |||
933 | rgb_matrix[3] = ideal[7]; | ||
934 | |||
935 | rgb_matrix[4] = dal_fixed31_32_mul( | ||
936 | multiplier, | ||
937 | dal_fixed31_32_sub( | ||
938 | dal_fixed31_32_mul( | ||
939 | ideal[8], | ||
940 | cos_grph_hue), | ||
941 | dal_fixed31_32_mul( | ||
942 | ideal[4], | ||
943 | sin_grph_hue))); | ||
944 | |||
945 | rgb_matrix[5] = dal_fixed31_32_mul( | ||
946 | multiplier, | ||
947 | dal_fixed31_32_sub( | ||
948 | dal_fixed31_32_mul( | ||
949 | ideal[9], | ||
950 | cos_grph_hue), | ||
951 | dal_fixed31_32_mul( | ||
952 | ideal[5], | ||
953 | sin_grph_hue))); | ||
954 | |||
955 | rgb_matrix[6] = dal_fixed31_32_mul( | ||
956 | multiplier, | ||
957 | dal_fixed31_32_sub( | ||
958 | dal_fixed31_32_mul( | ||
959 | ideal[10], | ||
960 | cos_grph_hue), | ||
961 | dal_fixed31_32_mul( | ||
962 | ideal[6], | ||
963 | sin_grph_hue))); | ||
964 | |||
965 | rgb_matrix[7] = ideal[11]; | ||
966 | } | ||
967 | |||
968 | static void calculate_yuv_matrix(struct core_color *core_color, | ||
969 | unsigned int sink_index, | ||
970 | enum dc_color_space color_space, | ||
971 | struct fixed31_32 *yuv_matrix) | ||
972 | { | ||
973 | struct fixed31_32 ideal[12]; | ||
974 | uint32_t i = 0; | ||
975 | |||
976 | if ((color_space == COLOR_SPACE_YPBPR601) || | ||
977 | (color_space == COLOR_SPACE_YCBCR601) || | ||
978 | (color_space == COLOR_SPACE_YCBCR601_LIMITED)) { | ||
979 | static const int32_t matrix_[] = { | ||
980 | 25578516, 50216016, 9752344, 6250000, | ||
981 | -14764391, -28985609, 43750000, 50000000, | ||
982 | 43750000, -36635164, -7114836, 50000000 | ||
983 | }; | ||
984 | do { | ||
985 | ideal[i] = dal_fixed31_32_from_fraction( | ||
986 | matrix_[i], | ||
987 | 100000000); | ||
988 | ++i; | ||
989 | } while (i != ARRAY_SIZE(matrix_)); | ||
990 | } else { | ||
991 | static const int32_t matrix_[] = { | ||
992 | 18187266, 61183125, 6176484, 6250000, | ||
993 | -10025059, -33724941, 43750000, 50000000, | ||
994 | 43750000, -39738379, -4011621, 50000000 | ||
995 | }; | ||
996 | do { | ||
997 | ideal[i] = dal_fixed31_32_from_fraction( | ||
998 | matrix_[i], | ||
999 | 100000000); | ||
1000 | ++i; | ||
1001 | } while (i != ARRAY_SIZE(matrix_)); | ||
1002 | } | ||
1003 | |||
1004 | struct fixed31_32 grph_cont; | ||
1005 | struct fixed31_32 grph_sat; | ||
1006 | struct fixed31_32 grph_bright; | ||
1007 | struct fixed31_32 sin_grph_hue; | ||
1008 | struct fixed31_32 cos_grph_hue; | ||
1009 | |||
1010 | initialize_fix_point_color_values( | ||
1011 | core_color, sink_index, &grph_cont, &grph_sat, | ||
1012 | &grph_bright, &sin_grph_hue, &cos_grph_hue); | ||
1013 | |||
1014 | const struct fixed31_32 multiplier = | ||
1015 | dal_fixed31_32_mul(grph_cont, grph_sat); | ||
1016 | |||
1017 | yuv_matrix[0] = dal_fixed31_32_mul(ideal[0], grph_cont); | ||
1018 | |||
1019 | yuv_matrix[1] = dal_fixed31_32_mul(ideal[1], grph_cont); | ||
1020 | |||
1021 | yuv_matrix[2] = dal_fixed31_32_mul(ideal[2], grph_cont); | ||
1022 | |||
1023 | yuv_matrix[4] = dal_fixed31_32_mul( | ||
1024 | multiplier, | ||
1025 | dal_fixed31_32_add( | ||
1026 | dal_fixed31_32_mul( | ||
1027 | ideal[4], | ||
1028 | cos_grph_hue), | ||
1029 | dal_fixed31_32_mul( | ||
1030 | ideal[8], | ||
1031 | sin_grph_hue))); | ||
1032 | |||
1033 | yuv_matrix[5] = dal_fixed31_32_mul( | ||
1034 | multiplier, | ||
1035 | dal_fixed31_32_add( | ||
1036 | dal_fixed31_32_mul( | ||
1037 | ideal[5], | ||
1038 | cos_grph_hue), | ||
1039 | dal_fixed31_32_mul( | ||
1040 | ideal[9], | ||
1041 | sin_grph_hue))); | ||
1042 | |||
1043 | yuv_matrix[6] = dal_fixed31_32_mul( | ||
1044 | multiplier, | ||
1045 | dal_fixed31_32_add( | ||
1046 | dal_fixed31_32_mul( | ||
1047 | ideal[6], | ||
1048 | cos_grph_hue), | ||
1049 | dal_fixed31_32_mul( | ||
1050 | ideal[10], | ||
1051 | sin_grph_hue))); | ||
1052 | |||
1053 | yuv_matrix[7] = ideal[7]; | ||
1054 | |||
1055 | yuv_matrix[8] = dal_fixed31_32_mul( | ||
1056 | multiplier, | ||
1057 | dal_fixed31_32_sub( | ||
1058 | dal_fixed31_32_mul( | ||
1059 | ideal[8], | ||
1060 | cos_grph_hue), | ||
1061 | dal_fixed31_32_mul( | ||
1062 | ideal[4], | ||
1063 | sin_grph_hue))); | ||
1064 | |||
1065 | yuv_matrix[9] = dal_fixed31_32_mul( | ||
1066 | multiplier, | ||
1067 | dal_fixed31_32_sub( | ||
1068 | dal_fixed31_32_mul( | ||
1069 | ideal[9], | ||
1070 | cos_grph_hue), | ||
1071 | dal_fixed31_32_mul( | ||
1072 | ideal[5], | ||
1073 | sin_grph_hue))); | ||
1074 | |||
1075 | yuv_matrix[10] = dal_fixed31_32_mul( | ||
1076 | multiplier, | ||
1077 | dal_fixed31_32_sub( | ||
1078 | dal_fixed31_32_mul( | ||
1079 | ideal[10], | ||
1080 | cos_grph_hue), | ||
1081 | dal_fixed31_32_mul( | ||
1082 | ideal[6], | ||
1083 | sin_grph_hue))); | ||
1084 | |||
1085 | yuv_matrix[11] = ideal[11]; | ||
1086 | |||
1087 | if ((color_space == COLOR_SPACE_YCBCR601_LIMITED) || | ||
1088 | (color_space == COLOR_SPACE_YCBCR709_LIMITED)) { | ||
1089 | yuv_matrix[3] = dal_fixed31_32_add(ideal[3], grph_bright); | ||
1090 | } else { | ||
1091 | yuv_matrix[3] = dal_fixed31_32_add( | ||
1092 | ideal[3], | ||
1093 | dal_fixed31_32_mul( | ||
1094 | grph_bright, | ||
1095 | dal_fixed31_32_from_fraction(86, 100))); | ||
1096 | } | ||
1097 | } | ||
1098 | |||
1099 | static void calculate_csc_matrix(struct core_color *core_color, | ||
1100 | unsigned int sink_index, | ||
1101 | enum dc_color_space color_space, | ||
1102 | uint16_t *csc_matrix) | ||
1103 | { | ||
1104 | struct fixed31_32 fixed_csc_matrix[12]; | ||
1105 | switch (color_space) { | ||
1106 | case COLOR_SPACE_SRGB: | ||
1107 | calculate_rgb_matrix_legacy | ||
1108 | (core_color, sink_index, fixed_csc_matrix); | ||
1109 | convert_float_matrix_legacy | ||
1110 | (csc_matrix, fixed_csc_matrix, 12); | ||
1111 | break; | ||
1112 | case COLOR_SPACE_SRGB_LIMITED: | ||
1113 | calculate_rgb_limited_range_matrix(core_color, sink_index, | ||
1114 | fixed_csc_matrix); | ||
1115 | convert_float_matrix(csc_matrix, fixed_csc_matrix, 12); | ||
1116 | break; | ||
1117 | case COLOR_SPACE_YCBCR601: | ||
1118 | case COLOR_SPACE_YCBCR709: | ||
1119 | case COLOR_SPACE_YCBCR601_LIMITED: | ||
1120 | case COLOR_SPACE_YCBCR709_LIMITED: | ||
1121 | case COLOR_SPACE_YPBPR601: | ||
1122 | case COLOR_SPACE_YPBPR709: | ||
1123 | calculate_yuv_matrix(core_color, sink_index, color_space, | ||
1124 | fixed_csc_matrix); | ||
1125 | convert_float_matrix(csc_matrix, fixed_csc_matrix, 12); | ||
1126 | break; | ||
1127 | default: | ||
1128 | calculate_rgb_matrix_legacy | ||
1129 | (core_color, sink_index, fixed_csc_matrix); | ||
1130 | convert_float_matrix_legacy | ||
1131 | (csc_matrix, fixed_csc_matrix, 12); | ||
1132 | break; | ||
1133 | } | ||
1134 | } | ||
1135 | |||
1136 | struct mod_color *mod_color_create(struct dc *dc) | ||
1137 | { | ||
1138 | int i = 0; | ||
1139 | struct core_color *core_color = | ||
1140 | dm_alloc(sizeof(struct core_color)); | ||
1141 | struct core_dc *core_dc = DC_TO_CORE(dc); | ||
1142 | struct persistent_data_flag flag; | ||
1143 | |||
1144 | if (core_color == NULL) | ||
1145 | goto fail_alloc_context; | ||
1146 | |||
1147 | core_color->caps = dm_alloc(sizeof(struct sink_caps) * | ||
1148 | MOD_COLOR_MAX_CONCURRENT_SINKS); | ||
1149 | |||
1150 | if (core_color->caps == NULL) | ||
1151 | goto fail_alloc_caps; | ||
1152 | |||
1153 | for (i = 0; i < MOD_COLOR_MAX_CONCURRENT_SINKS; i++) | ||
1154 | core_color->caps[i].sink = NULL; | ||
1155 | |||
1156 | core_color->state = dm_alloc(sizeof(struct color_state) * | ||
1157 | MOD_COLOR_MAX_CONCURRENT_SINKS); | ||
1158 | |||
1159 | /*hardcoded to sRGB with 6500 color temperature*/ | ||
1160 | for (i = 0; i < MOD_COLOR_MAX_CONCURRENT_SINKS; i++) { | ||
1161 | core_color->state[i].source_gamut.blueX = 1500; | ||
1162 | core_color->state[i].source_gamut.blueY = 600; | ||
1163 | core_color->state[i].source_gamut.greenX = 3000; | ||
1164 | core_color->state[i].source_gamut.greenY = 6000; | ||
1165 | core_color->state[i].source_gamut.redX = 6400; | ||
1166 | core_color->state[i].source_gamut.redY = 3300; | ||
1167 | core_color->state[i].source_gamut.whiteX = 3127; | ||
1168 | core_color->state[i].source_gamut.whiteY = 3290; | ||
1169 | |||
1170 | core_color->state[i].destination_gamut.blueX = 1500; | ||
1171 | core_color->state[i].destination_gamut.blueY = 600; | ||
1172 | core_color->state[i].destination_gamut.greenX = 3000; | ||
1173 | core_color->state[i].destination_gamut.greenY = 6000; | ||
1174 | core_color->state[i].destination_gamut.redX = 6400; | ||
1175 | core_color->state[i].destination_gamut.redY = 3300; | ||
1176 | core_color->state[i].destination_gamut.whiteX = 3127; | ||
1177 | core_color->state[i].destination_gamut.whiteY = 3290; | ||
1178 | |||
1179 | core_color->state[i].custom_color_temperature = 6500; | ||
1180 | |||
1181 | core_color->state[i].contrast.current = 100; | ||
1182 | core_color->state[i].contrast.min = 0; | ||
1183 | core_color->state[i].contrast.max = 200; | ||
1184 | |||
1185 | core_color->state[i].saturation.current = 100; | ||
1186 | core_color->state[i].saturation.min = 0; | ||
1187 | core_color->state[i].saturation.max = 200; | ||
1188 | |||
1189 | core_color->state[i].brightness.current = 0; | ||
1190 | core_color->state[i].brightness.min = -100; | ||
1191 | core_color->state[i].brightness.max = 100; | ||
1192 | |||
1193 | core_color->state[i].hue.current = 0; | ||
1194 | core_color->state[i].hue.min = -30; | ||
1195 | core_color->state[i].hue.max = 30; | ||
1196 | } | ||
1197 | |||
1198 | if (core_color->state == NULL) | ||
1199 | goto fail_alloc_state; | ||
1200 | |||
1201 | core_color->num_sinks = 0; | ||
1202 | |||
1203 | if (dc == NULL) | ||
1204 | goto fail_construct; | ||
1205 | |||
1206 | core_color->dc = dc; | ||
1207 | |||
1208 | if (!check_dc_support(dc)) | ||
1209 | goto fail_construct; | ||
1210 | |||
1211 | /* Create initial module folder in registry for color adjustment */ | ||
1212 | flag.save_per_edid = true; | ||
1213 | flag.save_per_link = false; | ||
1214 | |||
1215 | dm_write_persistent_data(core_dc->ctx, NULL, COLOR_REGISTRY_NAME, NULL, | ||
1216 | NULL, 0, &flag); | ||
1217 | |||
1218 | return &core_color->public; | ||
1219 | |||
1220 | fail_construct: | ||
1221 | dm_free(core_color->state); | ||
1222 | |||
1223 | fail_alloc_state: | ||
1224 | dm_free(core_color->caps); | ||
1225 | |||
1226 | fail_alloc_caps: | ||
1227 | dm_free(core_color); | ||
1228 | |||
1229 | fail_alloc_context: | ||
1230 | return NULL; | ||
1231 | } | ||
1232 | |||
1233 | void mod_color_destroy(struct mod_color *mod_color) | ||
1234 | { | ||
1235 | if (mod_color != NULL) { | ||
1236 | int i; | ||
1237 | struct core_color *core_color = | ||
1238 | MOD_COLOR_TO_CORE(mod_color); | ||
1239 | |||
1240 | dm_free(core_color->state); | ||
1241 | |||
1242 | for (i = 0; i < core_color->num_sinks; i++) | ||
1243 | dc_sink_release(core_color->caps[i].sink); | ||
1244 | |||
1245 | dm_free(core_color->caps); | ||
1246 | |||
1247 | dm_free(core_color); | ||
1248 | } | ||
1249 | } | ||
1250 | |||
1251 | bool mod_color_add_sink(struct mod_color *mod_color, const struct dc_sink *sink) | ||
1252 | { | ||
1253 | struct core_color *core_color = MOD_COLOR_TO_CORE(mod_color); | ||
1254 | struct core_dc *core_dc = DC_TO_CORE(core_color->dc); | ||
1255 | bool persistent_color_temp_enable; | ||
1256 | int persistent_custom_color_temp = 0; | ||
1257 | struct color_space_coordinates persistent_source_gamut; | ||
1258 | struct color_space_coordinates persistent_destination_gamut; | ||
1259 | int persistent_brightness; | ||
1260 | int persistent_contrast; | ||
1261 | int persistent_hue; | ||
1262 | int persistent_saturation; | ||
1263 | enum dc_quantization_range persistent_quantization_range; | ||
1264 | struct persistent_data_flag flag; | ||
1265 | |||
1266 | if (core_color->num_sinks < MOD_COLOR_MAX_CONCURRENT_SINKS) { | ||
1267 | dc_sink_retain(sink); | ||
1268 | core_color->caps[core_color->num_sinks].sink = sink; | ||
1269 | core_color->state[core_color->num_sinks]. | ||
1270 | user_enable_color_temperature = true; | ||
1271 | |||
1272 | /* get persistent data from registry */ | ||
1273 | flag.save_per_edid = true; | ||
1274 | flag.save_per_link = false; | ||
1275 | |||
1276 | |||
1277 | if (dm_read_persistent_data(core_dc->ctx, sink, | ||
1278 | COLOR_REGISTRY_NAME, | ||
1279 | "enablecolortempadj", | ||
1280 | &persistent_color_temp_enable, | ||
1281 | sizeof(bool), &flag)) | ||
1282 | core_color->state[core_color->num_sinks]. | ||
1283 | user_enable_color_temperature = | ||
1284 | persistent_color_temp_enable; | ||
1285 | else | ||
1286 | core_color->state[core_color->num_sinks]. | ||
1287 | user_enable_color_temperature = true; | ||
1288 | |||
1289 | if (dm_read_persistent_data(core_dc->ctx, sink, | ||
1290 | COLOR_REGISTRY_NAME, | ||
1291 | "customcolortemp", | ||
1292 | &persistent_custom_color_temp, | ||
1293 | sizeof(int), &flag)) | ||
1294 | core_color->state[core_color->num_sinks]. | ||
1295 | custom_color_temperature | ||
1296 | = persistent_custom_color_temp; | ||
1297 | else | ||
1298 | core_color->state[core_color->num_sinks]. | ||
1299 | custom_color_temperature = 6500; | ||
1300 | |||
1301 | if (dm_read_persistent_data(core_dc->ctx, sink, | ||
1302 | COLOR_REGISTRY_NAME, | ||
1303 | "sourcegamut", | ||
1304 | &persistent_source_gamut, | ||
1305 | sizeof(struct color_space_coordinates), | ||
1306 | &flag)) { | ||
1307 | memcpy(&core_color->state[core_color->num_sinks]. | ||
1308 | source_gamut, &persistent_source_gamut, | ||
1309 | sizeof(struct color_space_coordinates)); | ||
1310 | } else { | ||
1311 | core_color->state[core_color->num_sinks]. | ||
1312 | source_gamut.blueX = 1500; | ||
1313 | core_color->state[core_color->num_sinks]. | ||
1314 | source_gamut.blueY = 600; | ||
1315 | core_color->state[core_color->num_sinks]. | ||
1316 | source_gamut.greenX = 3000; | ||
1317 | core_color->state[core_color->num_sinks]. | ||
1318 | source_gamut.greenY = 6000; | ||
1319 | core_color->state[core_color->num_sinks]. | ||
1320 | source_gamut.redX = 6400; | ||
1321 | core_color->state[core_color->num_sinks]. | ||
1322 | source_gamut.redY = 3300; | ||
1323 | core_color->state[core_color->num_sinks]. | ||
1324 | source_gamut.whiteX = 3127; | ||
1325 | core_color->state[core_color->num_sinks]. | ||
1326 | source_gamut.whiteY = 3290; | ||
1327 | } | ||
1328 | |||
1329 | if (dm_read_persistent_data(core_dc->ctx, sink, COLOR_REGISTRY_NAME, | ||
1330 | "destgamut", | ||
1331 | &persistent_destination_gamut, | ||
1332 | sizeof(struct color_space_coordinates), | ||
1333 | &flag)) { | ||
1334 | memcpy(&core_color->state[core_color->num_sinks]. | ||
1335 | destination_gamut, | ||
1336 | &persistent_destination_gamut, | ||
1337 | sizeof(struct color_space_coordinates)); | ||
1338 | } else { | ||
1339 | core_color->state[core_color->num_sinks]. | ||
1340 | destination_gamut.blueX = 1500; | ||
1341 | core_color->state[core_color->num_sinks]. | ||
1342 | destination_gamut.blueY = 600; | ||
1343 | core_color->state[core_color->num_sinks]. | ||
1344 | destination_gamut.greenX = 3000; | ||
1345 | core_color->state[core_color->num_sinks]. | ||
1346 | destination_gamut.greenY = 6000; | ||
1347 | core_color->state[core_color->num_sinks]. | ||
1348 | destination_gamut.redX = 6400; | ||
1349 | core_color->state[core_color->num_sinks]. | ||
1350 | destination_gamut.redY = 3300; | ||
1351 | core_color->state[core_color->num_sinks]. | ||
1352 | destination_gamut.whiteX = 3127; | ||
1353 | core_color->state[core_color->num_sinks]. | ||
1354 | destination_gamut.whiteY = 3290; | ||
1355 | } | ||
1356 | |||
1357 | if (dm_read_persistent_data(core_dc->ctx, sink, COLOR_REGISTRY_NAME, | ||
1358 | "brightness", | ||
1359 | &persistent_brightness, | ||
1360 | sizeof(int), &flag)) | ||
1361 | core_color->state[core_color->num_sinks]. | ||
1362 | brightness.current = persistent_brightness; | ||
1363 | else | ||
1364 | core_color->state[core_color->num_sinks]. | ||
1365 | brightness.current = 0; | ||
1366 | |||
1367 | if (dm_read_persistent_data(core_dc->ctx, sink, COLOR_REGISTRY_NAME, | ||
1368 | "contrast", | ||
1369 | &persistent_contrast, | ||
1370 | sizeof(int), &flag)) | ||
1371 | core_color->state[core_color->num_sinks]. | ||
1372 | contrast.current = persistent_contrast; | ||
1373 | else | ||
1374 | core_color->state[core_color->num_sinks]. | ||
1375 | contrast.current = 100; | ||
1376 | |||
1377 | if (dm_read_persistent_data(core_dc->ctx, sink, COLOR_REGISTRY_NAME, | ||
1378 | "hue", | ||
1379 | &persistent_hue, | ||
1380 | sizeof(int), &flag)) | ||
1381 | core_color->state[core_color->num_sinks]. | ||
1382 | hue.current = persistent_hue; | ||
1383 | else | ||
1384 | core_color->state[core_color->num_sinks]. | ||
1385 | hue.current = 0; | ||
1386 | |||
1387 | if (dm_read_persistent_data(core_dc->ctx, sink, COLOR_REGISTRY_NAME, | ||
1388 | "saturation", | ||
1389 | &persistent_saturation, | ||
1390 | sizeof(int), &flag)) | ||
1391 | core_color->state[core_color->num_sinks]. | ||
1392 | saturation.current = persistent_saturation; | ||
1393 | else | ||
1394 | core_color->state[core_color->num_sinks]. | ||
1395 | saturation.current = 100; | ||
1396 | |||
1397 | if (dm_read_persistent_data(core_dc->ctx, sink, | ||
1398 | COLOR_REGISTRY_NAME, | ||
1399 | "preferred_quantization_range", | ||
1400 | &persistent_quantization_range, | ||
1401 | sizeof(int), &flag)) | ||
1402 | core_color->state[core_color->num_sinks]. | ||
1403 | preferred_quantization_range = | ||
1404 | persistent_quantization_range; | ||
1405 | else | ||
1406 | core_color->state[core_color->num_sinks]. | ||
1407 | preferred_quantization_range = QUANTIZATION_RANGE_FULL; | ||
1408 | |||
1409 | core_color->num_sinks++; | ||
1410 | return true; | ||
1411 | } | ||
1412 | return false; | ||
1413 | } | ||
1414 | |||
1415 | bool mod_color_remove_sink(struct mod_color *mod_color, | ||
1416 | const struct dc_sink *sink) | ||
1417 | { | ||
1418 | int i = 0, j = 0; | ||
1419 | struct core_color *core_color = MOD_COLOR_TO_CORE(mod_color); | ||
1420 | |||
1421 | for (i = 0; i < core_color->num_sinks; i++) { | ||
1422 | if (core_color->caps[i].sink == sink) { | ||
1423 | /* To remove this sink, shift everything after down */ | ||
1424 | for (j = i; j < core_color->num_sinks - 1; j++) { | ||
1425 | core_color->caps[j].sink = | ||
1426 | core_color->caps[j + 1].sink; | ||
1427 | |||
1428 | memcpy(&core_color->state[j], | ||
1429 | &core_color->state[j + 1], | ||
1430 | sizeof(struct color_state)); | ||
1431 | } | ||
1432 | |||
1433 | core_color->num_sinks--; | ||
1434 | |||
1435 | dc_sink_release(sink); | ||
1436 | |||
1437 | return true; | ||
1438 | } | ||
1439 | } | ||
1440 | |||
1441 | return false; | ||
1442 | } | ||
1443 | |||
1444 | bool mod_color_update_gamut_to_stream(struct mod_color *mod_color, | ||
1445 | const struct dc_stream **streams, int num_streams) | ||
1446 | { | ||
1447 | struct core_color *core_color = MOD_COLOR_TO_CORE(mod_color); | ||
1448 | struct core_dc *core_dc = DC_TO_CORE(core_color->dc); | ||
1449 | struct persistent_data_flag flag; | ||
1450 | struct gamut_src_dst_matrix *matrix = | ||
1451 | dm_alloc(sizeof(struct gamut_src_dst_matrix)); | ||
1452 | |||
1453 | unsigned int stream_index, sink_index, j; | ||
1454 | |||
1455 | for (stream_index = 0; stream_index < num_streams; stream_index++) { | ||
1456 | sink_index = sink_index_from_sink(core_color, | ||
1457 | streams[stream_index]->sink); | ||
1458 | |||
1459 | /* Write persistent data in registry*/ | ||
1460 | flag.save_per_edid = true; | ||
1461 | flag.save_per_link = false; | ||
1462 | |||
1463 | dm_write_persistent_data(core_dc->ctx, | ||
1464 | streams[stream_index]->sink, | ||
1465 | COLOR_REGISTRY_NAME, | ||
1466 | "sourcegamut", | ||
1467 | &core_color->state[sink_index]. | ||
1468 | source_gamut, | ||
1469 | sizeof(struct color_space_coordinates), | ||
1470 | &flag); | ||
1471 | |||
1472 | dm_write_persistent_data(core_dc->ctx, | ||
1473 | streams[stream_index]->sink, | ||
1474 | COLOR_REGISTRY_NAME, | ||
1475 | "destgamut", | ||
1476 | &core_color->state[sink_index]. | ||
1477 | destination_gamut, | ||
1478 | sizeof(struct color_space_coordinates), | ||
1479 | &flag); | ||
1480 | |||
1481 | if (!build_gamut_remap_matrix | ||
1482 | (core_color->state[sink_index].source_gamut, | ||
1483 | matrix->rgbCoeffSrc, | ||
1484 | matrix->whiteCoeffSrc)) | ||
1485 | goto function_fail; | ||
1486 | |||
1487 | if (!build_gamut_remap_matrix | ||
1488 | (core_color->state[sink_index]. | ||
1489 | destination_gamut, | ||
1490 | matrix->rgbCoeffDst, matrix->whiteCoeffDst)) | ||
1491 | goto function_fail; | ||
1492 | |||
1493 | struct fixed31_32 gamut_result[12]; | ||
1494 | struct fixed31_32 temp_matrix[9]; | ||
1495 | |||
1496 | if (!gamut_to_color_matrix( | ||
1497 | matrix->rgbCoeffDst, | ||
1498 | matrix->whiteCoeffDst, | ||
1499 | matrix->rgbCoeffSrc, | ||
1500 | matrix->whiteCoeffSrc, | ||
1501 | true, | ||
1502 | temp_matrix)) | ||
1503 | goto function_fail; | ||
1504 | |||
1505 | gamut_result[0] = temp_matrix[0]; | ||
1506 | gamut_result[1] = temp_matrix[1]; | ||
1507 | gamut_result[2] = temp_matrix[2]; | ||
1508 | gamut_result[3] = matrix->whiteCoeffSrc[0]; | ||
1509 | gamut_result[4] = temp_matrix[3]; | ||
1510 | gamut_result[5] = temp_matrix[4]; | ||
1511 | gamut_result[6] = temp_matrix[5]; | ||
1512 | gamut_result[7] = matrix->whiteCoeffSrc[1]; | ||
1513 | gamut_result[8] = temp_matrix[6]; | ||
1514 | gamut_result[9] = temp_matrix[7]; | ||
1515 | gamut_result[10] = temp_matrix[8]; | ||
1516 | gamut_result[11] = matrix->whiteCoeffSrc[2]; | ||
1517 | |||
1518 | struct core_stream *core_stream = | ||
1519 | DC_STREAM_TO_CORE | ||
1520 | (streams[stream_index]); | ||
1521 | |||
1522 | core_stream->public.gamut_remap_matrix.enable_remap = true; | ||
1523 | |||
1524 | for (j = 0; j < 12; j++) | ||
1525 | core_stream->public. | ||
1526 | gamut_remap_matrix.matrix[j] = | ||
1527 | gamut_result[j]; | ||
1528 | } | ||
1529 | |||
1530 | dm_free(matrix); | ||
1531 | core_color->dc->stream_funcs.set_gamut_remap | ||
1532 | (core_color->dc, streams, num_streams); | ||
1533 | |||
1534 | return true; | ||
1535 | |||
1536 | function_fail: | ||
1537 | dm_free(matrix); | ||
1538 | return false; | ||
1539 | } | ||
1540 | |||
1541 | bool mod_color_adjust_source_gamut(struct mod_color *mod_color, | ||
1542 | const struct dc_stream **streams, int num_streams, | ||
1543 | struct gamut_space_coordinates *input_gamut_coordinates, | ||
1544 | struct white_point_coodinates *input_white_point_coordinates) | ||
1545 | { | ||
1546 | struct core_color *core_color = MOD_COLOR_TO_CORE(mod_color); | ||
1547 | |||
1548 | unsigned int stream_index, sink_index; | ||
1549 | |||
1550 | for (stream_index = 0; stream_index < num_streams; stream_index++) { | ||
1551 | sink_index = sink_index_from_sink(core_color, | ||
1552 | streams[stream_index]->sink); | ||
1553 | |||
1554 | core_color->state[sink_index].source_gamut.blueX = | ||
1555 | input_gamut_coordinates->blueX; | ||
1556 | core_color->state[sink_index].source_gamut.blueY = | ||
1557 | input_gamut_coordinates->blueY; | ||
1558 | core_color->state[sink_index].source_gamut.greenX = | ||
1559 | input_gamut_coordinates->greenX; | ||
1560 | core_color->state[sink_index].source_gamut.greenY = | ||
1561 | input_gamut_coordinates->greenY; | ||
1562 | core_color->state[sink_index].source_gamut.redX = | ||
1563 | input_gamut_coordinates->redX; | ||
1564 | core_color->state[sink_index].source_gamut.redY = | ||
1565 | input_gamut_coordinates->redY; | ||
1566 | core_color->state[sink_index].source_gamut.whiteX = | ||
1567 | input_white_point_coordinates->whiteX; | ||
1568 | core_color->state[sink_index].source_gamut.whiteY = | ||
1569 | input_white_point_coordinates->whiteY; | ||
1570 | } | ||
1571 | |||
1572 | if (!mod_color_update_gamut_to_stream(mod_color, streams, num_streams)) | ||
1573 | return false; | ||
1574 | |||
1575 | return true; | ||
1576 | } | ||
1577 | |||
1578 | bool mod_color_adjust_destination_gamut(struct mod_color *mod_color, | ||
1579 | const struct dc_stream **streams, int num_streams, | ||
1580 | struct gamut_space_coordinates *input_gamut_coordinates, | ||
1581 | struct white_point_coodinates *input_white_point_coordinates) | ||
1582 | { | ||
1583 | struct core_color *core_color = MOD_COLOR_TO_CORE(mod_color); | ||
1584 | |||
1585 | unsigned int stream_index, sink_index; | ||
1586 | |||
1587 | for (stream_index = 0; stream_index < num_streams; stream_index++) { | ||
1588 | sink_index = sink_index_from_sink(core_color, | ||
1589 | streams[stream_index]->sink); | ||
1590 | |||
1591 | core_color->state[sink_index].destination_gamut.blueX = | ||
1592 | input_gamut_coordinates->blueX; | ||
1593 | core_color->state[sink_index].destination_gamut.blueY = | ||
1594 | input_gamut_coordinates->blueY; | ||
1595 | core_color->state[sink_index].destination_gamut.greenX = | ||
1596 | input_gamut_coordinates->greenX; | ||
1597 | core_color->state[sink_index].destination_gamut.greenY = | ||
1598 | input_gamut_coordinates->greenY; | ||
1599 | core_color->state[sink_index].destination_gamut.redX = | ||
1600 | input_gamut_coordinates->redX; | ||
1601 | core_color->state[sink_index].destination_gamut.redY = | ||
1602 | input_gamut_coordinates->redY; | ||
1603 | core_color->state[sink_index].destination_gamut.whiteX = | ||
1604 | input_white_point_coordinates->whiteX; | ||
1605 | core_color->state[sink_index].destination_gamut.whiteY = | ||
1606 | input_white_point_coordinates->whiteY; | ||
1607 | } | ||
1608 | |||
1609 | if (!mod_color_update_gamut_to_stream(mod_color, streams, num_streams)) | ||
1610 | return false; | ||
1611 | |||
1612 | return true; | ||
1613 | } | ||
1614 | |||
1615 | bool mod_color_set_white_point(struct mod_color *mod_color, | ||
1616 | const struct dc_stream **streams, int num_streams, | ||
1617 | struct white_point_coodinates *white_point) | ||
1618 | { | ||
1619 | struct core_color *core_color = MOD_COLOR_TO_CORE(mod_color); | ||
1620 | |||
1621 | unsigned int stream_index, sink_index; | ||
1622 | |||
1623 | for (stream_index = 0; stream_index < num_streams; | ||
1624 | stream_index++) { | ||
1625 | sink_index = sink_index_from_sink(core_color, | ||
1626 | streams[stream_index]->sink); | ||
1627 | core_color->state[sink_index].source_gamut.whiteX = | ||
1628 | white_point->whiteX; | ||
1629 | core_color->state[sink_index].source_gamut.whiteY = | ||
1630 | white_point->whiteY; | ||
1631 | } | ||
1632 | |||
1633 | if (!mod_color_update_gamut_to_stream(mod_color, streams, num_streams)) | ||
1634 | return false; | ||
1635 | |||
1636 | return true; | ||
1637 | } | ||
1638 | |||
1639 | bool mod_color_set_user_enable(struct mod_color *mod_color, | ||
1640 | const struct dc_stream **streams, int num_streams, | ||
1641 | bool user_enable) | ||
1642 | { | ||
1643 | struct core_color *core_color = | ||
1644 | MOD_COLOR_TO_CORE(mod_color); | ||
1645 | struct core_dc *core_dc = DC_TO_CORE(core_color->dc); | ||
1646 | struct persistent_data_flag flag; | ||
1647 | unsigned int stream_index, sink_index; | ||
1648 | |||
1649 | for (stream_index = 0; stream_index < num_streams; stream_index++) { | ||
1650 | sink_index = sink_index_from_sink(core_color, | ||
1651 | streams[stream_index]->sink); | ||
1652 | core_color->state[sink_index].user_enable_color_temperature | ||
1653 | = user_enable; | ||
1654 | |||
1655 | /* Write persistent data in registry*/ | ||
1656 | flag.save_per_edid = true; | ||
1657 | flag.save_per_link = false; | ||
1658 | |||
1659 | dm_write_persistent_data(core_dc->ctx, | ||
1660 | streams[stream_index]->sink, | ||
1661 | COLOR_REGISTRY_NAME, | ||
1662 | "enablecolortempadj", | ||
1663 | &user_enable, | ||
1664 | sizeof(bool), | ||
1665 | &flag); | ||
1666 | } | ||
1667 | return true; | ||
1668 | } | ||
1669 | |||
1670 | bool mod_color_get_user_enable(struct mod_color *mod_color, | ||
1671 | const struct dc_sink *sink, | ||
1672 | bool *user_enable) | ||
1673 | { | ||
1674 | struct core_color *core_color = | ||
1675 | MOD_COLOR_TO_CORE(mod_color); | ||
1676 | |||
1677 | unsigned int sink_index = sink_index_from_sink(core_color, sink); | ||
1678 | |||
1679 | *user_enable = core_color->state[sink_index]. | ||
1680 | user_enable_color_temperature; | ||
1681 | |||
1682 | return true; | ||
1683 | } | ||
1684 | |||
1685 | bool mod_color_get_custom_color_temperature(struct mod_color *mod_color, | ||
1686 | const struct dc_sink *sink, | ||
1687 | int *color_temperature) | ||
1688 | { | ||
1689 | struct core_color *core_color = | ||
1690 | MOD_COLOR_TO_CORE(mod_color); | ||
1691 | |||
1692 | unsigned int sink_index = sink_index_from_sink(core_color, sink); | ||
1693 | |||
1694 | *color_temperature = core_color->state[sink_index]. | ||
1695 | custom_color_temperature; | ||
1696 | |||
1697 | return true; | ||
1698 | } | ||
1699 | |||
1700 | bool mod_color_set_custom_color_temperature(struct mod_color *mod_color, | ||
1701 | const struct dc_stream **streams, int num_streams, | ||
1702 | int color_temperature) | ||
1703 | { | ||
1704 | struct core_color *core_color = | ||
1705 | MOD_COLOR_TO_CORE(mod_color); | ||
1706 | struct core_dc *core_dc = DC_TO_CORE(core_color->dc); | ||
1707 | struct persistent_data_flag flag; | ||
1708 | unsigned int stream_index, sink_index; | ||
1709 | |||
1710 | for (stream_index = 0; stream_index < num_streams; stream_index++) { | ||
1711 | sink_index = sink_index_from_sink(core_color, | ||
1712 | streams[stream_index]->sink); | ||
1713 | core_color->state[sink_index].custom_color_temperature | ||
1714 | = color_temperature; | ||
1715 | |||
1716 | /* Write persistent data in registry*/ | ||
1717 | flag.save_per_edid = true; | ||
1718 | flag.save_per_link = false; | ||
1719 | |||
1720 | dm_write_persistent_data(core_dc->ctx, | ||
1721 | streams[stream_index]->sink, | ||
1722 | COLOR_REGISTRY_NAME, | ||
1723 | "customcolortemp", | ||
1724 | &color_temperature, | ||
1725 | sizeof(int), | ||
1726 | &flag); | ||
1727 | } | ||
1728 | return true; | ||
1729 | } | ||
1730 | |||
1731 | bool mod_color_get_color_saturation(struct mod_color *mod_color, | ||
1732 | const struct dc_sink *sink, | ||
1733 | struct color_range *color_saturation) | ||
1734 | { | ||
1735 | struct core_color *core_color = | ||
1736 | MOD_COLOR_TO_CORE(mod_color); | ||
1737 | |||
1738 | unsigned int sink_index = sink_index_from_sink(core_color, sink); | ||
1739 | |||
1740 | *color_saturation = core_color->state[sink_index].saturation; | ||
1741 | |||
1742 | return true; | ||
1743 | } | ||
1744 | |||
1745 | bool mod_color_get_color_contrast(struct mod_color *mod_color, | ||
1746 | const struct dc_sink *sink, | ||
1747 | struct color_range *color_contrast) | ||
1748 | { | ||
1749 | struct core_color *core_color = | ||
1750 | MOD_COLOR_TO_CORE(mod_color); | ||
1751 | |||
1752 | unsigned int sink_index = sink_index_from_sink(core_color, sink); | ||
1753 | |||
1754 | *color_contrast = core_color->state[sink_index].contrast; | ||
1755 | |||
1756 | return true; | ||
1757 | } | ||
1758 | |||
1759 | bool mod_color_get_color_brightness(struct mod_color *mod_color, | ||
1760 | const struct dc_sink *sink, | ||
1761 | struct color_range *color_brightness) | ||
1762 | { | ||
1763 | struct core_color *core_color = | ||
1764 | MOD_COLOR_TO_CORE(mod_color); | ||
1765 | |||
1766 | unsigned int sink_index = sink_index_from_sink(core_color, sink); | ||
1767 | |||
1768 | *color_brightness = core_color->state[sink_index].brightness; | ||
1769 | |||
1770 | return true; | ||
1771 | } | ||
1772 | |||
1773 | bool mod_color_get_color_hue(struct mod_color *mod_color, | ||
1774 | const struct dc_sink *sink, | ||
1775 | struct color_range *color_hue) | ||
1776 | { | ||
1777 | struct core_color *core_color = | ||
1778 | MOD_COLOR_TO_CORE(mod_color); | ||
1779 | |||
1780 | unsigned int sink_index = sink_index_from_sink(core_color, sink); | ||
1781 | |||
1782 | *color_hue = core_color->state[sink_index].hue; | ||
1783 | |||
1784 | return true; | ||
1785 | } | ||
1786 | |||
1787 | bool mod_color_get_source_gamut(struct mod_color *mod_color, | ||
1788 | const struct dc_sink *sink, | ||
1789 | struct color_space_coordinates *source_gamut) | ||
1790 | { | ||
1791 | struct core_color *core_color = | ||
1792 | MOD_COLOR_TO_CORE(mod_color); | ||
1793 | |||
1794 | unsigned int sink_index = sink_index_from_sink(core_color, sink); | ||
1795 | |||
1796 | *source_gamut = core_color->state[sink_index].source_gamut; | ||
1797 | |||
1798 | return true; | ||
1799 | } | ||
1800 | |||
1801 | bool mod_color_notify_mode_change(struct mod_color *mod_color, | ||
1802 | const struct dc_stream **streams, int num_streams) | ||
1803 | { | ||
1804 | struct core_color *core_color = MOD_COLOR_TO_CORE(mod_color); | ||
1805 | |||
1806 | struct gamut_src_dst_matrix *matrix = | ||
1807 | dm_alloc(sizeof(struct gamut_src_dst_matrix)); | ||
1808 | |||
1809 | unsigned int stream_index, sink_index, j; | ||
1810 | |||
1811 | for (stream_index = 0; stream_index < num_streams; stream_index++) { | ||
1812 | sink_index = sink_index_from_sink(core_color, | ||
1813 | streams[stream_index]->sink); | ||
1814 | |||
1815 | if (!build_gamut_remap_matrix | ||
1816 | (core_color->state[sink_index].source_gamut, | ||
1817 | matrix->rgbCoeffSrc, | ||
1818 | matrix->whiteCoeffSrc)) | ||
1819 | goto function_fail; | ||
1820 | |||
1821 | if (!build_gamut_remap_matrix | ||
1822 | (core_color->state[sink_index]. | ||
1823 | destination_gamut, | ||
1824 | matrix->rgbCoeffDst, matrix->whiteCoeffDst)) | ||
1825 | goto function_fail; | ||
1826 | |||
1827 | struct fixed31_32 gamut_result[12]; | ||
1828 | struct fixed31_32 temp_matrix[9]; | ||
1829 | |||
1830 | if (!gamut_to_color_matrix( | ||
1831 | matrix->rgbCoeffDst, | ||
1832 | matrix->whiteCoeffDst, | ||
1833 | matrix->rgbCoeffSrc, | ||
1834 | matrix->whiteCoeffSrc, | ||
1835 | true, | ||
1836 | temp_matrix)) | ||
1837 | goto function_fail; | ||
1838 | |||
1839 | gamut_result[0] = temp_matrix[0]; | ||
1840 | gamut_result[1] = temp_matrix[1]; | ||
1841 | gamut_result[2] = temp_matrix[2]; | ||
1842 | gamut_result[3] = matrix->whiteCoeffSrc[0]; | ||
1843 | gamut_result[4] = temp_matrix[3]; | ||
1844 | gamut_result[5] = temp_matrix[4]; | ||
1845 | gamut_result[6] = temp_matrix[5]; | ||
1846 | gamut_result[7] = matrix->whiteCoeffSrc[1]; | ||
1847 | gamut_result[8] = temp_matrix[6]; | ||
1848 | gamut_result[9] = temp_matrix[7]; | ||
1849 | gamut_result[10] = temp_matrix[8]; | ||
1850 | gamut_result[11] = matrix->whiteCoeffSrc[2]; | ||
1851 | |||
1852 | |||
1853 | struct core_stream *core_stream = | ||
1854 | DC_STREAM_TO_CORE | ||
1855 | (streams[stream_index]); | ||
1856 | |||
1857 | core_stream->public.gamut_remap_matrix.enable_remap = true; | ||
1858 | |||
1859 | for (j = 0; j < 12; j++) | ||
1860 | core_stream->public. | ||
1861 | gamut_remap_matrix.matrix[j] = | ||
1862 | gamut_result[j]; | ||
1863 | |||
1864 | calculate_csc_matrix(core_color, sink_index, | ||
1865 | core_stream->public.output_color_space, | ||
1866 | core_stream->public.csc_color_matrix.matrix); | ||
1867 | |||
1868 | core_stream->public.csc_color_matrix.enable_adjustment = true; | ||
1869 | } | ||
1870 | |||
1871 | dm_free(matrix); | ||
1872 | |||
1873 | return true; | ||
1874 | |||
1875 | function_fail: | ||
1876 | dm_free(matrix); | ||
1877 | return false; | ||
1878 | } | ||
1879 | |||
1880 | bool mod_color_set_brightness(struct mod_color *mod_color, | ||
1881 | const struct dc_stream **streams, int num_streams, | ||
1882 | int brightness_value) | ||
1883 | { | ||
1884 | struct core_color *core_color = MOD_COLOR_TO_CORE(mod_color); | ||
1885 | struct core_dc *core_dc = DC_TO_CORE(core_color->dc); | ||
1886 | struct persistent_data_flag flag; | ||
1887 | unsigned int stream_index, sink_index; | ||
1888 | |||
1889 | for (stream_index = 0; stream_index < num_streams; stream_index++) { | ||
1890 | sink_index = sink_index_from_sink(core_color, | ||
1891 | streams[stream_index]->sink); | ||
1892 | |||
1893 | struct core_stream *core_stream = | ||
1894 | DC_STREAM_TO_CORE | ||
1895 | (streams[stream_index]); | ||
1896 | |||
1897 | core_color->state[sink_index].brightness.current = | ||
1898 | brightness_value; | ||
1899 | |||
1900 | calculate_csc_matrix(core_color, sink_index, | ||
1901 | core_stream->public.output_color_space, | ||
1902 | core_stream->public.csc_color_matrix.matrix); | ||
1903 | |||
1904 | core_stream->public.csc_color_matrix.enable_adjustment = true; | ||
1905 | |||
1906 | /* Write persistent data in registry*/ | ||
1907 | flag.save_per_edid = true; | ||
1908 | flag.save_per_link = false; | ||
1909 | dm_write_persistent_data(core_dc->ctx, | ||
1910 | streams[stream_index]->sink, | ||
1911 | COLOR_REGISTRY_NAME, | ||
1912 | "brightness", | ||
1913 | &brightness_value, | ||
1914 | sizeof(int), | ||
1915 | &flag); | ||
1916 | } | ||
1917 | |||
1918 | core_color->dc->stream_funcs.set_gamut_remap | ||
1919 | (core_color->dc, streams, num_streams); | ||
1920 | |||
1921 | return true; | ||
1922 | } | ||
1923 | |||
1924 | bool mod_color_set_contrast(struct mod_color *mod_color, | ||
1925 | const struct dc_stream **streams, int num_streams, | ||
1926 | int contrast_value) | ||
1927 | { | ||
1928 | struct core_color *core_color = MOD_COLOR_TO_CORE(mod_color); | ||
1929 | struct core_dc *core_dc = DC_TO_CORE(core_color->dc); | ||
1930 | struct persistent_data_flag flag; | ||
1931 | unsigned int stream_index, sink_index; | ||
1932 | |||
1933 | for (stream_index = 0; stream_index < num_streams; stream_index++) { | ||
1934 | sink_index = sink_index_from_sink(core_color, | ||
1935 | streams[stream_index]->sink); | ||
1936 | |||
1937 | struct core_stream *core_stream = | ||
1938 | DC_STREAM_TO_CORE | ||
1939 | (streams[stream_index]); | ||
1940 | |||
1941 | core_color->state[sink_index].contrast.current = | ||
1942 | contrast_value; | ||
1943 | |||
1944 | calculate_csc_matrix(core_color, sink_index, | ||
1945 | core_stream->public.output_color_space, | ||
1946 | core_stream->public.csc_color_matrix.matrix); | ||
1947 | |||
1948 | core_stream->public.csc_color_matrix.enable_adjustment = true; | ||
1949 | |||
1950 | /* Write persistent data in registry*/ | ||
1951 | flag.save_per_edid = true; | ||
1952 | flag.save_per_link = false; | ||
1953 | dm_write_persistent_data(core_dc->ctx, | ||
1954 | streams[stream_index]->sink, | ||
1955 | COLOR_REGISTRY_NAME, | ||
1956 | "contrast", | ||
1957 | &contrast_value, | ||
1958 | sizeof(int), | ||
1959 | &flag); | ||
1960 | } | ||
1961 | |||
1962 | core_color->dc->stream_funcs.set_gamut_remap | ||
1963 | (core_color->dc, streams, num_streams); | ||
1964 | |||
1965 | return true; | ||
1966 | } | ||
1967 | |||
1968 | bool mod_color_set_hue(struct mod_color *mod_color, | ||
1969 | const struct dc_stream **streams, int num_streams, | ||
1970 | int hue_value) | ||
1971 | { | ||
1972 | struct core_color *core_color = MOD_COLOR_TO_CORE(mod_color); | ||
1973 | struct core_dc *core_dc = DC_TO_CORE(core_color->dc); | ||
1974 | struct persistent_data_flag flag; | ||
1975 | unsigned int stream_index, sink_index; | ||
1976 | |||
1977 | for (stream_index = 0; stream_index < num_streams; stream_index++) { | ||
1978 | sink_index = sink_index_from_sink(core_color, | ||
1979 | streams[stream_index]->sink); | ||
1980 | |||
1981 | struct core_stream *core_stream = | ||
1982 | DC_STREAM_TO_CORE | ||
1983 | (streams[stream_index]); | ||
1984 | |||
1985 | core_color->state[sink_index].hue.current = hue_value; | ||
1986 | |||
1987 | calculate_csc_matrix(core_color, sink_index, | ||
1988 | core_stream->public.output_color_space, | ||
1989 | core_stream->public.csc_color_matrix.matrix); | ||
1990 | |||
1991 | core_stream->public.csc_color_matrix.enable_adjustment = true; | ||
1992 | |||
1993 | /* Write persistent data in registry*/ | ||
1994 | flag.save_per_edid = true; | ||
1995 | flag.save_per_link = false; | ||
1996 | dm_write_persistent_data(core_dc->ctx, | ||
1997 | streams[stream_index]->sink, | ||
1998 | COLOR_REGISTRY_NAME, | ||
1999 | "hue", | ||
2000 | &hue_value, | ||
2001 | sizeof(int), | ||
2002 | &flag); | ||
2003 | } | ||
2004 | |||
2005 | core_color->dc->stream_funcs.set_gamut_remap | ||
2006 | (core_color->dc, streams, num_streams); | ||
2007 | |||
2008 | return true; | ||
2009 | } | ||
2010 | |||
2011 | bool mod_color_set_saturation(struct mod_color *mod_color, | ||
2012 | const struct dc_stream **streams, int num_streams, | ||
2013 | int saturation_value) | ||
2014 | { | ||
2015 | struct core_color *core_color = MOD_COLOR_TO_CORE(mod_color); | ||
2016 | struct core_dc *core_dc = DC_TO_CORE(core_color->dc); | ||
2017 | struct persistent_data_flag flag; | ||
2018 | unsigned int stream_index, sink_index; | ||
2019 | |||
2020 | for (stream_index = 0; stream_index < num_streams; stream_index++) { | ||
2021 | sink_index = sink_index_from_sink(core_color, | ||
2022 | streams[stream_index]->sink); | ||
2023 | |||
2024 | struct core_stream *core_stream = | ||
2025 | DC_STREAM_TO_CORE | ||
2026 | (streams[stream_index]); | ||
2027 | |||
2028 | core_color->state[sink_index].saturation.current = | ||
2029 | saturation_value; | ||
2030 | |||
2031 | calculate_csc_matrix(core_color, sink_index, | ||
2032 | core_stream->public.output_color_space, | ||
2033 | core_stream->public.csc_color_matrix.matrix); | ||
2034 | |||
2035 | core_stream->public.csc_color_matrix.enable_adjustment = true; | ||
2036 | |||
2037 | /* Write persistent data in registry*/ | ||
2038 | flag.save_per_edid = true; | ||
2039 | flag.save_per_link = false; | ||
2040 | dm_write_persistent_data(core_dc->ctx, | ||
2041 | streams[stream_index]->sink, | ||
2042 | COLOR_REGISTRY_NAME, | ||
2043 | "saturation", | ||
2044 | &saturation_value, | ||
2045 | sizeof(int), | ||
2046 | &flag); | ||
2047 | } | ||
2048 | |||
2049 | core_color->dc->stream_funcs.set_gamut_remap | ||
2050 | (core_color->dc, streams, num_streams); | ||
2051 | |||
2052 | return true; | ||
2053 | } | ||
2054 | |||
2055 | bool mod_color_set_preferred_quantization_range(struct mod_color *mod_color, | ||
2056 | const struct dc_sink *sink, | ||
2057 | enum dc_quantization_range quantization_range) | ||
2058 | { | ||
2059 | struct core_color *core_color = MOD_COLOR_TO_CORE(mod_color); | ||
2060 | struct core_dc *core_dc = DC_TO_CORE(core_color->dc); | ||
2061 | struct persistent_data_flag flag; | ||
2062 | unsigned int sink_index; | ||
2063 | |||
2064 | sink_index = sink_index_from_sink(core_color, sink); | ||
2065 | if (core_color->state[sink_index]. | ||
2066 | preferred_quantization_range != quantization_range) { | ||
2067 | core_color->state[sink_index].preferred_quantization_range = | ||
2068 | quantization_range; | ||
2069 | flag.save_per_edid = true; | ||
2070 | flag.save_per_link = false; | ||
2071 | dm_write_persistent_data(core_dc->ctx, | ||
2072 | sink, | ||
2073 | COLOR_REGISTRY_NAME, | ||
2074 | "quantization_range", | ||
2075 | &quantization_range, | ||
2076 | sizeof(int), | ||
2077 | &flag); | ||
2078 | } | ||
2079 | |||
2080 | return true; | ||
2081 | } | ||
2082 | |||
2083 | bool mod_color_get_preferred_quantization_range(struct mod_color *mod_color, | ||
2084 | const struct dc_sink *sink, | ||
2085 | enum dc_quantization_range *quantization_range) | ||
2086 | { | ||
2087 | struct core_color *core_color = MOD_COLOR_TO_CORE(mod_color); | ||
2088 | unsigned int sink_index; | ||
2089 | |||
2090 | sink_index = sink_index_from_sink(core_color, sink); | ||
2091 | *quantization_range = core_color->state[sink_index]. | ||
2092 | preferred_quantization_range; | ||
2093 | return true; | ||
2094 | } | ||
diff --git a/drivers/gpu/drm/amd/display/modules/freesync/Makefile b/drivers/gpu/drm/amd/display/modules/freesync/Makefile new file mode 100644 index 000000000000..db8e0ff6d7a9 --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/freesync/Makefile | |||
@@ -0,0 +1,10 @@ | |||
1 | # | ||
2 | # Makefile for the 'freesync' sub-module of DAL. | ||
3 | # | ||
4 | |||
5 | FREESYNC = freesync.o | ||
6 | |||
7 | AMD_DAL_FREESYNC = $(addprefix $(AMDDALPATH)/modules/freesync/,$(FREESYNC)) | ||
8 | #$(info ************ DAL-FREE SYNC_MAKEFILE ************) | ||
9 | |||
10 | AMD_DISPLAY_FILES += $(AMD_DAL_FREESYNC) | ||
diff --git a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c new file mode 100644 index 000000000000..eb912baa0169 --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c | |||
@@ -0,0 +1,1158 @@ | |||
1 | /* | ||
2 | * Copyright 2016 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 "dm_services.h" | ||
27 | #include "dc.h" | ||
28 | #include "mod_freesync.h" | ||
29 | #include "core_types.h" | ||
30 | #include "core_dc.h" | ||
31 | |||
32 | #define MOD_FREESYNC_MAX_CONCURRENT_STREAMS 32 | ||
33 | |||
34 | /* Refresh rate ramp at a fixed rate of 65 Hz/second */ | ||
35 | #define STATIC_SCREEN_RAMP_DELTA_REFRESH_RATE_PER_FRAME ((1000 / 60) * 65) | ||
36 | /* Number of elements in the render times cache array */ | ||
37 | #define RENDER_TIMES_MAX_COUNT 20 | ||
38 | /* Threshold to exit BTR (to avoid frequent enter-exits at the lower limit) */ | ||
39 | #define BTR_EXIT_MARGIN 2000 | ||
40 | |||
41 | #define FREESYNC_REGISTRY_NAME "freesync_v1" | ||
42 | |||
43 | struct gradual_static_ramp { | ||
44 | bool ramp_is_active; | ||
45 | bool ramp_direction_is_up; | ||
46 | unsigned int ramp_current_frame_duration_in_ns; | ||
47 | }; | ||
48 | |||
49 | struct time_cache { | ||
50 | /* video (48Hz feature) related */ | ||
51 | unsigned int update_duration_in_ns; | ||
52 | |||
53 | /* BTR/fixed refresh related */ | ||
54 | unsigned int prev_time_stamp_in_us; | ||
55 | |||
56 | unsigned int min_render_time_in_us; | ||
57 | unsigned int max_render_time_in_us; | ||
58 | |||
59 | unsigned int render_times_index; | ||
60 | unsigned int render_times[RENDER_TIMES_MAX_COUNT]; | ||
61 | }; | ||
62 | |||
63 | struct below_the_range { | ||
64 | bool btr_active; | ||
65 | bool program_btr; | ||
66 | |||
67 | unsigned int mid_point_in_us; | ||
68 | |||
69 | unsigned int inserted_frame_duration_in_us; | ||
70 | unsigned int frames_to_insert; | ||
71 | unsigned int frame_counter; | ||
72 | }; | ||
73 | |||
74 | struct fixed_refresh { | ||
75 | bool fixed_refresh_active; | ||
76 | bool program_fixed_refresh; | ||
77 | }; | ||
78 | |||
79 | struct freesync_state { | ||
80 | bool fullscreen; | ||
81 | bool static_screen; | ||
82 | bool video; | ||
83 | |||
84 | unsigned int nominal_refresh_rate_in_micro_hz; | ||
85 | bool windowed_fullscreen; | ||
86 | |||
87 | struct time_cache time; | ||
88 | |||
89 | struct gradual_static_ramp static_ramp; | ||
90 | struct below_the_range btr; | ||
91 | struct fixed_refresh fixed_refresh; | ||
92 | }; | ||
93 | |||
94 | struct freesync_entity { | ||
95 | const struct dc_stream *stream; | ||
96 | struct mod_freesync_caps *caps; | ||
97 | struct freesync_state state; | ||
98 | struct mod_freesync_user_enable user_enable; | ||
99 | }; | ||
100 | |||
101 | struct core_freesync { | ||
102 | struct mod_freesync public; | ||
103 | struct dc *dc; | ||
104 | struct freesync_entity *map; | ||
105 | int num_entities; | ||
106 | }; | ||
107 | |||
108 | #define MOD_FREESYNC_TO_CORE(mod_freesync)\ | ||
109 | container_of(mod_freesync, struct core_freesync, public) | ||
110 | |||
111 | static bool check_dc_support(const struct dc *dc) | ||
112 | { | ||
113 | if (dc->stream_funcs.adjust_vmin_vmax == NULL) | ||
114 | return false; | ||
115 | |||
116 | return true; | ||
117 | } | ||
118 | |||
119 | struct mod_freesync *mod_freesync_create(struct dc *dc) | ||
120 | { | ||
121 | struct core_freesync *core_freesync = | ||
122 | dm_alloc(sizeof(struct core_freesync)); | ||
123 | |||
124 | struct core_dc *core_dc = DC_TO_CORE(dc); | ||
125 | |||
126 | struct persistent_data_flag flag; | ||
127 | |||
128 | int i = 0; | ||
129 | |||
130 | if (core_freesync == NULL) | ||
131 | goto fail_alloc_context; | ||
132 | |||
133 | core_freesync->map = dm_alloc(sizeof(struct freesync_entity) * | ||
134 | MOD_FREESYNC_MAX_CONCURRENT_STREAMS); | ||
135 | |||
136 | if (core_freesync->map == NULL) | ||
137 | goto fail_alloc_map; | ||
138 | |||
139 | for (i = 0; i < MOD_FREESYNC_MAX_CONCURRENT_STREAMS; i++) | ||
140 | core_freesync->map[i].stream = NULL; | ||
141 | |||
142 | core_freesync->num_entities = 0; | ||
143 | |||
144 | if (dc == NULL) | ||
145 | goto fail_construct; | ||
146 | |||
147 | core_freesync->dc = dc; | ||
148 | |||
149 | if (!check_dc_support(dc)) | ||
150 | goto fail_construct; | ||
151 | |||
152 | /* Create initial module folder in registry for freesync enable data */ | ||
153 | flag.save_per_edid = true; | ||
154 | flag.save_per_link = false; | ||
155 | dm_write_persistent_data(core_dc->ctx, NULL, FREESYNC_REGISTRY_NAME, NULL, NULL, | ||
156 | 0, &flag); | ||
157 | |||
158 | return &core_freesync->public; | ||
159 | |||
160 | fail_construct: | ||
161 | dm_free(core_freesync->map); | ||
162 | |||
163 | fail_alloc_map: | ||
164 | dm_free(core_freesync); | ||
165 | |||
166 | fail_alloc_context: | ||
167 | return NULL; | ||
168 | } | ||
169 | |||
170 | void mod_freesync_destroy(struct mod_freesync *mod_freesync) | ||
171 | { | ||
172 | if (mod_freesync != NULL) { | ||
173 | int i; | ||
174 | struct core_freesync *core_freesync = | ||
175 | MOD_FREESYNC_TO_CORE(mod_freesync); | ||
176 | |||
177 | for (i = 0; i < core_freesync->num_entities; i++) | ||
178 | if (core_freesync->map[i].stream) | ||
179 | dc_stream_release(core_freesync->map[i].stream); | ||
180 | |||
181 | dm_free(core_freesync->map); | ||
182 | |||
183 | dm_free(core_freesync); | ||
184 | } | ||
185 | } | ||
186 | |||
187 | /* Given a specific dc_stream* this function finds its equivalent | ||
188 | * on the core_freesync->map and returns the corresponding index | ||
189 | */ | ||
190 | static unsigned int map_index_from_stream(struct core_freesync *core_freesync, | ||
191 | const struct dc_stream *stream) | ||
192 | { | ||
193 | unsigned int index = 0; | ||
194 | |||
195 | for (index = 0; index < core_freesync->num_entities; index++) { | ||
196 | if (core_freesync->map[index].stream == stream) { | ||
197 | return index; | ||
198 | } | ||
199 | } | ||
200 | /* Could not find stream requested */ | ||
201 | ASSERT(false); | ||
202 | return index; | ||
203 | } | ||
204 | |||
205 | bool mod_freesync_add_stream(struct mod_freesync *mod_freesync, | ||
206 | const struct dc_stream *stream, struct mod_freesync_caps *caps) | ||
207 | { | ||
208 | struct core_freesync *core_freesync = | ||
209 | MOD_FREESYNC_TO_CORE(mod_freesync); | ||
210 | struct core_stream *core_stream = | ||
211 | DC_STREAM_TO_CORE(stream); | ||
212 | struct core_dc *core_dc = DC_TO_CORE(core_freesync->dc); | ||
213 | |||
214 | int persistent_freesync_enable = 0; | ||
215 | struct persistent_data_flag flag; | ||
216 | |||
217 | flag.save_per_edid = true; | ||
218 | flag.save_per_link = false; | ||
219 | |||
220 | if (core_freesync->num_entities < MOD_FREESYNC_MAX_CONCURRENT_STREAMS) { | ||
221 | |||
222 | dc_stream_retain(stream); | ||
223 | |||
224 | core_freesync->map[core_freesync->num_entities].stream = stream; | ||
225 | core_freesync->map[core_freesync->num_entities].caps = caps; | ||
226 | |||
227 | core_freesync->map[core_freesync->num_entities].state. | ||
228 | fullscreen = false; | ||
229 | core_freesync->map[core_freesync->num_entities].state. | ||
230 | static_screen = false; | ||
231 | core_freesync->map[core_freesync->num_entities].state. | ||
232 | video = false; | ||
233 | core_freesync->map[core_freesync->num_entities].state.time. | ||
234 | update_duration_in_ns = 0; | ||
235 | core_freesync->map[core_freesync->num_entities].state. | ||
236 | static_ramp.ramp_is_active = false; | ||
237 | |||
238 | /* get persistent data from registry */ | ||
239 | if (dm_read_persistent_data(core_dc->ctx, stream->sink, | ||
240 | FREESYNC_REGISTRY_NAME, | ||
241 | "userenable", &persistent_freesync_enable, | ||
242 | sizeof(int), &flag)) { | ||
243 | core_freesync->map[core_freesync->num_entities].user_enable. | ||
244 | enable_for_gaming = | ||
245 | (persistent_freesync_enable & 1) ? true : false; | ||
246 | core_freesync->map[core_freesync->num_entities].user_enable. | ||
247 | enable_for_static = | ||
248 | (persistent_freesync_enable & 2) ? true : false; | ||
249 | core_freesync->map[core_freesync->num_entities].user_enable. | ||
250 | enable_for_video = | ||
251 | (persistent_freesync_enable & 4) ? true : false; | ||
252 | } else { | ||
253 | core_freesync->map[core_freesync->num_entities].user_enable. | ||
254 | enable_for_gaming = false; | ||
255 | core_freesync->map[core_freesync->num_entities].user_enable. | ||
256 | enable_for_static = false; | ||
257 | core_freesync->map[core_freesync->num_entities].user_enable. | ||
258 | enable_for_video = false; | ||
259 | } | ||
260 | |||
261 | if (caps->supported) | ||
262 | core_stream->public.ignore_msa_timing_param = 1; | ||
263 | |||
264 | core_freesync->num_entities++; | ||
265 | return true; | ||
266 | } | ||
267 | return false; | ||
268 | } | ||
269 | |||
270 | bool mod_freesync_remove_stream(struct mod_freesync *mod_freesync, | ||
271 | const struct dc_stream *stream) | ||
272 | { | ||
273 | struct core_freesync *core_freesync = | ||
274 | MOD_FREESYNC_TO_CORE(mod_freesync); | ||
275 | |||
276 | int i = 0; | ||
277 | unsigned int index = map_index_from_stream(core_freesync, stream); | ||
278 | dc_stream_release(core_freesync->map[index].stream); | ||
279 | core_freesync->map[index].stream = NULL; | ||
280 | /* To remove this entity, shift everything after down */ | ||
281 | for (i = index; i < core_freesync->num_entities - 1; i++) | ||
282 | core_freesync->map[i] = core_freesync->map[i + 1]; | ||
283 | core_freesync->num_entities--; | ||
284 | return true; | ||
285 | } | ||
286 | |||
287 | static void update_stream_freesync_context(struct core_freesync *core_freesync, | ||
288 | const struct dc_stream *stream) | ||
289 | { | ||
290 | unsigned int index; | ||
291 | struct freesync_context *ctx; | ||
292 | struct core_stream *core_stream; | ||
293 | |||
294 | core_stream = DC_STREAM_TO_CORE(stream); | ||
295 | ctx = &core_stream->public.freesync_ctx; | ||
296 | |||
297 | index = map_index_from_stream(core_freesync, stream); | ||
298 | |||
299 | ctx->supported = core_freesync->map[index].caps->supported; | ||
300 | ctx->enabled = (core_freesync->map[index].user_enable.enable_for_gaming || | ||
301 | core_freesync->map[index].user_enable.enable_for_video || | ||
302 | core_freesync->map[index].user_enable.enable_for_static); | ||
303 | ctx->active = (core_freesync->map[index].state.fullscreen || | ||
304 | core_freesync->map[index].state.video || | ||
305 | core_freesync->map[index].state.static_ramp.ramp_is_active); | ||
306 | ctx->min_refresh_in_micro_hz = | ||
307 | core_freesync->map[index].caps->min_refresh_in_micro_hz; | ||
308 | ctx->nominal_refresh_in_micro_hz = core_freesync-> | ||
309 | map[index].state.nominal_refresh_rate_in_micro_hz; | ||
310 | |||
311 | } | ||
312 | |||
313 | static void update_stream(struct core_freesync *core_freesync, | ||
314 | const struct dc_stream *stream) | ||
315 | { | ||
316 | struct core_stream *core_stream = DC_STREAM_TO_CORE(stream); | ||
317 | |||
318 | unsigned int index = map_index_from_stream(core_freesync, stream); | ||
319 | if (core_freesync->map[index].caps->supported) { | ||
320 | core_stream->public.ignore_msa_timing_param = 1; | ||
321 | update_stream_freesync_context(core_freesync, stream); | ||
322 | } | ||
323 | } | ||
324 | |||
325 | static void calc_vmin_vmax(struct core_freesync *core_freesync, | ||
326 | const struct dc_stream *stream, int *vmin, int *vmax) | ||
327 | { | ||
328 | unsigned int min_frame_duration_in_ns = 0, max_frame_duration_in_ns = 0; | ||
329 | unsigned int index = map_index_from_stream(core_freesync, stream); | ||
330 | |||
331 | min_frame_duration_in_ns = ((unsigned int) (div64_u64( | ||
332 | (1000000000ULL * 1000000), | ||
333 | core_freesync->map[index].state. | ||
334 | nominal_refresh_rate_in_micro_hz))); | ||
335 | max_frame_duration_in_ns = ((unsigned int) (div64_u64( | ||
336 | (1000000000ULL * 1000000), | ||
337 | core_freesync->map[index].caps->min_refresh_in_micro_hz))); | ||
338 | |||
339 | *vmax = div64_u64(div64_u64(((unsigned long long)( | ||
340 | max_frame_duration_in_ns) * stream->timing.pix_clk_khz), | ||
341 | stream->timing.h_total), 1000000); | ||
342 | *vmin = div64_u64(div64_u64(((unsigned long long)( | ||
343 | min_frame_duration_in_ns) * stream->timing.pix_clk_khz), | ||
344 | stream->timing.h_total), 1000000); | ||
345 | } | ||
346 | |||
347 | static void calc_v_total_from_duration(const struct dc_stream *stream, | ||
348 | unsigned int duration_in_ns, int *v_total_nominal) | ||
349 | { | ||
350 | *v_total_nominal = div64_u64(div64_u64(((unsigned long long)( | ||
351 | duration_in_ns) * stream->timing.pix_clk_khz), | ||
352 | stream->timing.h_total), 1000000); | ||
353 | } | ||
354 | |||
355 | static void calc_v_total_for_static_ramp(struct core_freesync *core_freesync, | ||
356 | const struct dc_stream *stream, | ||
357 | unsigned int index, int *v_total) | ||
358 | { | ||
359 | unsigned int frame_duration = 0; | ||
360 | |||
361 | struct gradual_static_ramp *static_ramp_variables = | ||
362 | &core_freesync->map[index].state.static_ramp; | ||
363 | |||
364 | /* Calc ratio between new and current frame duration with 3 digit */ | ||
365 | unsigned int frame_duration_ratio = div64_u64(1000000, | ||
366 | (1000 + div64_u64(((unsigned long long)( | ||
367 | STATIC_SCREEN_RAMP_DELTA_REFRESH_RATE_PER_FRAME) * | ||
368 | static_ramp_variables->ramp_current_frame_duration_in_ns), | ||
369 | 1000000000))); | ||
370 | |||
371 | /* Calculate delta between new and current frame duration in ns */ | ||
372 | unsigned int frame_duration_delta = div64_u64(((unsigned long long)( | ||
373 | static_ramp_variables->ramp_current_frame_duration_in_ns) * | ||
374 | (1000 - frame_duration_ratio)), 1000); | ||
375 | |||
376 | /* Adjust frame duration delta based on ratio between current and | ||
377 | * standard frame duration (frame duration at 60 Hz refresh rate). | ||
378 | */ | ||
379 | unsigned int ramp_rate_interpolated = div64_u64(((unsigned long long)( | ||
380 | frame_duration_delta) * static_ramp_variables-> | ||
381 | ramp_current_frame_duration_in_ns), 16666666); | ||
382 | |||
383 | /* Going to a higher refresh rate (lower frame duration) */ | ||
384 | if (static_ramp_variables->ramp_direction_is_up) { | ||
385 | /* reduce frame duration */ | ||
386 | static_ramp_variables->ramp_current_frame_duration_in_ns -= | ||
387 | ramp_rate_interpolated; | ||
388 | |||
389 | /* min frame duration */ | ||
390 | frame_duration = ((unsigned int) (div64_u64( | ||
391 | (1000000000ULL * 1000000), | ||
392 | core_freesync->map[index].state. | ||
393 | nominal_refresh_rate_in_micro_hz))); | ||
394 | |||
395 | /* adjust for frame duration below min */ | ||
396 | if (static_ramp_variables->ramp_current_frame_duration_in_ns <= | ||
397 | frame_duration) { | ||
398 | |||
399 | static_ramp_variables->ramp_is_active = false; | ||
400 | static_ramp_variables-> | ||
401 | ramp_current_frame_duration_in_ns = | ||
402 | frame_duration; | ||
403 | } | ||
404 | /* Going to a lower refresh rate (larger frame duration) */ | ||
405 | } else { | ||
406 | /* increase frame duration */ | ||
407 | static_ramp_variables->ramp_current_frame_duration_in_ns += | ||
408 | ramp_rate_interpolated; | ||
409 | |||
410 | /* max frame duration */ | ||
411 | frame_duration = ((unsigned int) (div64_u64( | ||
412 | (1000000000ULL * 1000000), | ||
413 | core_freesync->map[index].caps->min_refresh_in_micro_hz))); | ||
414 | |||
415 | /* adjust for frame duration above max */ | ||
416 | if (static_ramp_variables->ramp_current_frame_duration_in_ns >= | ||
417 | frame_duration) { | ||
418 | |||
419 | static_ramp_variables->ramp_is_active = false; | ||
420 | static_ramp_variables-> | ||
421 | ramp_current_frame_duration_in_ns = | ||
422 | frame_duration; | ||
423 | } | ||
424 | } | ||
425 | |||
426 | calc_v_total_from_duration(stream, static_ramp_variables-> | ||
427 | ramp_current_frame_duration_in_ns, v_total); | ||
428 | } | ||
429 | |||
430 | static void reset_freesync_state_variables(struct freesync_state* state) | ||
431 | { | ||
432 | state->static_ramp.ramp_is_active = false; | ||
433 | if (state->nominal_refresh_rate_in_micro_hz) | ||
434 | state->static_ramp.ramp_current_frame_duration_in_ns = | ||
435 | ((unsigned int) (div64_u64( | ||
436 | (1000000000ULL * 1000000), | ||
437 | state->nominal_refresh_rate_in_micro_hz))); | ||
438 | |||
439 | state->btr.btr_active = false; | ||
440 | state->btr.frame_counter = 0; | ||
441 | state->btr.frames_to_insert = 0; | ||
442 | state->btr.inserted_frame_duration_in_us = 0; | ||
443 | state->btr.program_btr = false; | ||
444 | |||
445 | state->fixed_refresh.fixed_refresh_active = false; | ||
446 | state->fixed_refresh.program_fixed_refresh = false; | ||
447 | } | ||
448 | /* | ||
449 | * Sets freesync mode on a stream depending on current freesync state. | ||
450 | */ | ||
451 | static bool set_freesync_on_streams(struct core_freesync *core_freesync, | ||
452 | const struct dc_stream **streams, int num_streams) | ||
453 | { | ||
454 | int v_total_nominal = 0, v_total_min = 0, v_total_max = 0; | ||
455 | unsigned int stream_idx, map_index = 0; | ||
456 | struct freesync_state *state; | ||
457 | |||
458 | if (num_streams == 0 || streams == NULL || num_streams > 1) | ||
459 | return false; | ||
460 | |||
461 | for (stream_idx = 0; stream_idx < num_streams; stream_idx++) { | ||
462 | |||
463 | map_index = map_index_from_stream(core_freesync, | ||
464 | streams[stream_idx]); | ||
465 | |||
466 | state = &core_freesync->map[map_index].state; | ||
467 | |||
468 | if (core_freesync->map[map_index].caps->supported) { | ||
469 | |||
470 | /* Fullscreen has the topmost priority. If the | ||
471 | * fullscreen bit is set, we are in a fullscreen | ||
472 | * application where it should not matter if it is | ||
473 | * static screen. We should not check the static_screen | ||
474 | * or video bit. | ||
475 | * | ||
476 | * Special cases of fullscreen include btr and fixed | ||
477 | * refresh. We program btr on every flip and involves | ||
478 | * programming full range right before the last inserted frame. | ||
479 | * However, we do not want to program the full freesync range | ||
480 | * when fixed refresh is active, because we only program | ||
481 | * that logic once and this will override it. | ||
482 | */ | ||
483 | if (core_freesync->map[map_index].user_enable. | ||
484 | enable_for_gaming == true && | ||
485 | state->fullscreen == true && | ||
486 | state->fixed_refresh.fixed_refresh_active == false) { | ||
487 | /* Enable freesync */ | ||
488 | |||
489 | calc_vmin_vmax(core_freesync, | ||
490 | streams[stream_idx], | ||
491 | &v_total_min, &v_total_max); | ||
492 | |||
493 | /* Update the freesync context for the stream */ | ||
494 | update_stream_freesync_context(core_freesync, | ||
495 | streams[stream_idx]); | ||
496 | |||
497 | core_freesync->dc->stream_funcs. | ||
498 | adjust_vmin_vmax(core_freesync->dc, streams, | ||
499 | num_streams, v_total_min, | ||
500 | v_total_max); | ||
501 | |||
502 | return true; | ||
503 | |||
504 | } else if (core_freesync->map[map_index].user_enable. | ||
505 | enable_for_video && state->video == true) { | ||
506 | /* Enable 48Hz feature */ | ||
507 | |||
508 | calc_v_total_from_duration(streams[stream_idx], | ||
509 | state->time.update_duration_in_ns, | ||
510 | &v_total_nominal); | ||
511 | |||
512 | /* Program only if v_total_nominal is in range*/ | ||
513 | if (v_total_nominal >= | ||
514 | streams[stream_idx]->timing.v_total) { | ||
515 | |||
516 | /* Update the freesync context for | ||
517 | * the stream | ||
518 | */ | ||
519 | update_stream_freesync_context( | ||
520 | core_freesync, | ||
521 | streams[stream_idx]); | ||
522 | |||
523 | core_freesync->dc->stream_funcs. | ||
524 | adjust_vmin_vmax( | ||
525 | core_freesync->dc, streams, | ||
526 | num_streams, v_total_nominal, | ||
527 | v_total_nominal); | ||
528 | } | ||
529 | return true; | ||
530 | |||
531 | } else { | ||
532 | /* Disable freesync */ | ||
533 | v_total_nominal = streams[stream_idx]-> | ||
534 | timing.v_total; | ||
535 | |||
536 | /* Update the freesync context for | ||
537 | * the stream | ||
538 | */ | ||
539 | update_stream_freesync_context( | ||
540 | core_freesync, | ||
541 | streams[stream_idx]); | ||
542 | |||
543 | core_freesync->dc->stream_funcs. | ||
544 | adjust_vmin_vmax( | ||
545 | core_freesync->dc, streams, | ||
546 | num_streams, v_total_nominal, | ||
547 | v_total_nominal); | ||
548 | |||
549 | /* Reset the cached variables */ | ||
550 | reset_freesync_state_variables(state); | ||
551 | |||
552 | return true; | ||
553 | } | ||
554 | } else { | ||
555 | /* Disable freesync */ | ||
556 | v_total_nominal = streams[stream_idx]-> | ||
557 | timing.v_total; | ||
558 | /* | ||
559 | * we have to reset drr always even sink does | ||
560 | * not support freesync because a former stream has | ||
561 | * be programmed | ||
562 | */ | ||
563 | core_freesync->dc->stream_funcs. | ||
564 | adjust_vmin_vmax( | ||
565 | core_freesync->dc, streams, | ||
566 | num_streams, v_total_nominal, | ||
567 | v_total_nominal); | ||
568 | /* Reset the cached variables */ | ||
569 | reset_freesync_state_variables(state); | ||
570 | } | ||
571 | |||
572 | } | ||
573 | |||
574 | return false; | ||
575 | } | ||
576 | |||
577 | static void set_static_ramp_variables(struct core_freesync *core_freesync, | ||
578 | unsigned int index, bool enable_static_screen) | ||
579 | { | ||
580 | unsigned int frame_duration = 0; | ||
581 | |||
582 | struct gradual_static_ramp *static_ramp_variables = | ||
583 | &core_freesync->map[index].state.static_ramp; | ||
584 | |||
585 | /* If ramp is not active, set initial frame duration depending on | ||
586 | * whether we are enabling/disabling static screen mode. If the ramp is | ||
587 | * already active, ramp should continue in the opposite direction | ||
588 | * starting with the current frame duration | ||
589 | */ | ||
590 | if (!static_ramp_variables->ramp_is_active) { | ||
591 | |||
592 | static_ramp_variables->ramp_is_active = true; | ||
593 | |||
594 | if (enable_static_screen == true) { | ||
595 | /* Going to lower refresh rate, so start from max | ||
596 | * refresh rate (min frame duration) | ||
597 | */ | ||
598 | frame_duration = ((unsigned int) (div64_u64( | ||
599 | (1000000000ULL * 1000000), | ||
600 | core_freesync->map[index].state. | ||
601 | nominal_refresh_rate_in_micro_hz))); | ||
602 | } else { | ||
603 | /* Going to higher refresh rate, so start from min | ||
604 | * refresh rate (max frame duration) | ||
605 | */ | ||
606 | frame_duration = ((unsigned int) (div64_u64( | ||
607 | (1000000000ULL * 1000000), | ||
608 | core_freesync->map[index].caps->min_refresh_in_micro_hz))); | ||
609 | } | ||
610 | |||
611 | static_ramp_variables-> | ||
612 | ramp_current_frame_duration_in_ns = frame_duration; | ||
613 | } | ||
614 | |||
615 | /* If we are ENABLING static screen, refresh rate should go DOWN. | ||
616 | * If we are DISABLING static screen, refresh rate should go UP. | ||
617 | */ | ||
618 | static_ramp_variables->ramp_direction_is_up = !enable_static_screen; | ||
619 | } | ||
620 | |||
621 | void mod_freesync_handle_v_update(struct mod_freesync *mod_freesync, | ||
622 | const struct dc_stream **streams, int num_streams) | ||
623 | { | ||
624 | struct core_freesync *core_freesync = | ||
625 | MOD_FREESYNC_TO_CORE(mod_freesync); | ||
626 | |||
627 | unsigned int index, v_total = 0; | ||
628 | struct freesync_state *state; | ||
629 | |||
630 | if (core_freesync->num_entities == 0) | ||
631 | return; | ||
632 | |||
633 | index = map_index_from_stream(core_freesync, | ||
634 | streams[0]); | ||
635 | |||
636 | if (core_freesync->map[index].caps->supported == false) | ||
637 | return; | ||
638 | |||
639 | state = &core_freesync->map[index].state; | ||
640 | |||
641 | /* Below the Range Logic */ | ||
642 | |||
643 | /* Only execute if in fullscreen mode */ | ||
644 | if (state->fullscreen == true && | ||
645 | core_freesync->map[index].user_enable.enable_for_gaming) { | ||
646 | |||
647 | if (state->btr.btr_active) | ||
648 | if (state->btr.frame_counter > 0) | ||
649 | |||
650 | state->btr.frame_counter--; | ||
651 | |||
652 | if (state->btr.frame_counter == 1) { | ||
653 | |||
654 | /* Restore FreeSync */ | ||
655 | set_freesync_on_streams(core_freesync, streams, | ||
656 | num_streams); | ||
657 | } | ||
658 | } | ||
659 | |||
660 | /* If in fullscreen freesync mode or in video, do not program | ||
661 | * static screen ramp values | ||
662 | */ | ||
663 | if (state->fullscreen == true || state->video == true) { | ||
664 | |||
665 | state->static_ramp.ramp_is_active = false; | ||
666 | |||
667 | return; | ||
668 | } | ||
669 | |||
670 | /* Gradual Static Screen Ramping Logic */ | ||
671 | |||
672 | /* Execute if ramp is active and user enabled freesync static screen*/ | ||
673 | if (state->static_ramp.ramp_is_active && | ||
674 | core_freesync->map[index].user_enable.enable_for_static) { | ||
675 | |||
676 | calc_v_total_for_static_ramp(core_freesync, streams[0], | ||
677 | index, &v_total); | ||
678 | |||
679 | /* Update the freesync context for the stream */ | ||
680 | update_stream_freesync_context(core_freesync, streams[0]); | ||
681 | |||
682 | /* Program static screen ramp values */ | ||
683 | core_freesync->dc->stream_funcs.adjust_vmin_vmax( | ||
684 | core_freesync->dc, streams, | ||
685 | num_streams, v_total, | ||
686 | v_total); | ||
687 | } | ||
688 | } | ||
689 | |||
690 | void mod_freesync_update_state(struct mod_freesync *mod_freesync, | ||
691 | const struct dc_stream **streams, int num_streams, | ||
692 | struct mod_freesync_params *freesync_params) | ||
693 | { | ||
694 | struct core_freesync *core_freesync = | ||
695 | MOD_FREESYNC_TO_CORE(mod_freesync); | ||
696 | bool freesync_program_required = false; | ||
697 | unsigned int stream_index; | ||
698 | struct freesync_state *state; | ||
699 | |||
700 | if (core_freesync->num_entities == 0) | ||
701 | return; | ||
702 | |||
703 | for(stream_index = 0; stream_index < num_streams; stream_index++) { | ||
704 | |||
705 | unsigned int map_index = map_index_from_stream(core_freesync, | ||
706 | streams[stream_index]); | ||
707 | |||
708 | state = &core_freesync->map[map_index].state; | ||
709 | |||
710 | switch (freesync_params->state){ | ||
711 | case FREESYNC_STATE_FULLSCREEN: | ||
712 | state->fullscreen = freesync_params->enable; | ||
713 | freesync_program_required = true; | ||
714 | state->windowed_fullscreen = | ||
715 | freesync_params->windowed_fullscreen; | ||
716 | break; | ||
717 | case FREESYNC_STATE_STATIC_SCREEN: | ||
718 | /* Static screen ramp is only enabled for embedded | ||
719 | * panels. Also change core variables only if there | ||
720 | * is a change. | ||
721 | */ | ||
722 | if (dc_is_embedded_signal( | ||
723 | streams[stream_index]->sink->sink_signal) && | ||
724 | state->static_screen != | ||
725 | freesync_params->enable) { | ||
726 | |||
727 | /* Change the state flag */ | ||
728 | state->static_screen = freesync_params->enable; | ||
729 | |||
730 | /* Change static screen ramp variables */ | ||
731 | set_static_ramp_variables(core_freesync, | ||
732 | map_index, | ||
733 | freesync_params->enable); | ||
734 | } | ||
735 | /* We program the ramp starting next VUpdate */ | ||
736 | break; | ||
737 | case FREESYNC_STATE_VIDEO: | ||
738 | /* Change core variables only if there is a change*/ | ||
739 | if(freesync_params->update_duration_in_ns != | ||
740 | state->time.update_duration_in_ns) { | ||
741 | |||
742 | state->video = freesync_params->enable; | ||
743 | state->time.update_duration_in_ns = | ||
744 | freesync_params->update_duration_in_ns; | ||
745 | |||
746 | freesync_program_required = true; | ||
747 | } | ||
748 | break; | ||
749 | } | ||
750 | } | ||
751 | |||
752 | if (freesync_program_required) | ||
753 | /* Program freesync according to current state*/ | ||
754 | set_freesync_on_streams(core_freesync, streams, num_streams); | ||
755 | } | ||
756 | |||
757 | |||
758 | bool mod_freesync_get_state(struct mod_freesync *mod_freesync, | ||
759 | const struct dc_stream *stream, | ||
760 | struct mod_freesync_params *freesync_params) | ||
761 | { | ||
762 | struct core_freesync *core_freesync = | ||
763 | MOD_FREESYNC_TO_CORE(mod_freesync); | ||
764 | |||
765 | unsigned int index = map_index_from_stream(core_freesync, stream); | ||
766 | |||
767 | if (core_freesync->map[index].state.fullscreen) { | ||
768 | freesync_params->state = FREESYNC_STATE_FULLSCREEN; | ||
769 | freesync_params->enable = true; | ||
770 | } else if (core_freesync->map[index].state.static_screen) { | ||
771 | freesync_params->state = FREESYNC_STATE_STATIC_SCREEN; | ||
772 | freesync_params->enable = true; | ||
773 | } else if (core_freesync->map[index].state.video) { | ||
774 | freesync_params->state = FREESYNC_STATE_VIDEO; | ||
775 | freesync_params->enable = true; | ||
776 | } else { | ||
777 | freesync_params->state = FREESYNC_STATE_NONE; | ||
778 | freesync_params->enable = false; | ||
779 | } | ||
780 | |||
781 | freesync_params->update_duration_in_ns = | ||
782 | core_freesync->map[index].state.time.update_duration_in_ns; | ||
783 | |||
784 | return true; | ||
785 | } | ||
786 | |||
787 | bool mod_freesync_set_user_enable(struct mod_freesync *mod_freesync, | ||
788 | const struct dc_stream **streams, int num_streams, | ||
789 | struct mod_freesync_user_enable *user_enable) | ||
790 | { | ||
791 | struct core_freesync *core_freesync = | ||
792 | MOD_FREESYNC_TO_CORE(mod_freesync); | ||
793 | struct core_dc *core_dc = DC_TO_CORE(core_freesync->dc); | ||
794 | |||
795 | unsigned int stream_index, map_index; | ||
796 | int persistent_data = 0; | ||
797 | struct persistent_data_flag flag; | ||
798 | |||
799 | flag.save_per_edid = true; | ||
800 | flag.save_per_link = false; | ||
801 | |||
802 | for(stream_index = 0; stream_index < num_streams; | ||
803 | stream_index++){ | ||
804 | |||
805 | map_index = map_index_from_stream(core_freesync, | ||
806 | streams[stream_index]); | ||
807 | |||
808 | core_freesync->map[map_index].user_enable = *user_enable; | ||
809 | |||
810 | /* Write persistent data in registry*/ | ||
811 | if (core_freesync->map[map_index].user_enable. | ||
812 | enable_for_gaming) | ||
813 | persistent_data = persistent_data | 1; | ||
814 | if (core_freesync->map[map_index].user_enable. | ||
815 | enable_for_static) | ||
816 | persistent_data = persistent_data | 2; | ||
817 | if (core_freesync->map[map_index].user_enable. | ||
818 | enable_for_video) | ||
819 | persistent_data = persistent_data | 4; | ||
820 | |||
821 | dm_write_persistent_data(core_dc->ctx, | ||
822 | streams[stream_index]->sink, | ||
823 | FREESYNC_REGISTRY_NAME, | ||
824 | "userenable", | ||
825 | &persistent_data, | ||
826 | sizeof(int), | ||
827 | &flag); | ||
828 | } | ||
829 | |||
830 | set_freesync_on_streams(core_freesync, streams, num_streams); | ||
831 | |||
832 | return true; | ||
833 | } | ||
834 | |||
835 | bool mod_freesync_get_user_enable(struct mod_freesync *mod_freesync, | ||
836 | const struct dc_stream *stream, | ||
837 | struct mod_freesync_user_enable *user_enable) | ||
838 | { | ||
839 | struct core_freesync *core_freesync = | ||
840 | MOD_FREESYNC_TO_CORE(mod_freesync); | ||
841 | |||
842 | unsigned int index = map_index_from_stream(core_freesync, stream); | ||
843 | |||
844 | *user_enable = core_freesync->map[index].user_enable; | ||
845 | |||
846 | return true; | ||
847 | } | ||
848 | |||
849 | void mod_freesync_notify_mode_change(struct mod_freesync *mod_freesync, | ||
850 | const struct dc_stream **streams, int num_streams) | ||
851 | { | ||
852 | struct core_freesync *core_freesync = | ||
853 | MOD_FREESYNC_TO_CORE(mod_freesync); | ||
854 | |||
855 | unsigned int stream_index, map_index; | ||
856 | unsigned min_frame_duration_in_ns, max_frame_duration_in_ns; | ||
857 | struct freesync_state *state; | ||
858 | |||
859 | for (stream_index = 0; stream_index < num_streams; stream_index++) { | ||
860 | |||
861 | map_index = map_index_from_stream(core_freesync, | ||
862 | streams[stream_index]); | ||
863 | |||
864 | state = &core_freesync->map[map_index].state; | ||
865 | |||
866 | if (core_freesync->map[map_index].caps->supported) { | ||
867 | /* Update the field rate for new timing */ | ||
868 | state->nominal_refresh_rate_in_micro_hz = 1000000 * | ||
869 | div64_u64(div64_u64((streams[stream_index]-> | ||
870 | timing.pix_clk_khz * 1000), | ||
871 | streams[stream_index]->timing.v_total), | ||
872 | streams[stream_index]->timing.h_total); | ||
873 | |||
874 | /* Update the stream */ | ||
875 | update_stream(core_freesync, streams[stream_index]); | ||
876 | |||
877 | /* Determine whether BTR can be supported */ | ||
878 | min_frame_duration_in_ns = ((unsigned int) (div64_u64( | ||
879 | (1000000000ULL * 1000000), | ||
880 | state->nominal_refresh_rate_in_micro_hz))); | ||
881 | |||
882 | max_frame_duration_in_ns = ((unsigned int) (div64_u64( | ||
883 | (1000000000ULL * 1000000), | ||
884 | core_freesync->map[map_index].caps->min_refresh_in_micro_hz))); | ||
885 | |||
886 | if (max_frame_duration_in_ns >= | ||
887 | 2 * min_frame_duration_in_ns) | ||
888 | core_freesync->map[map_index].caps->btr_supported = true; | ||
889 | else | ||
890 | core_freesync->map[map_index].caps->btr_supported = false; | ||
891 | |||
892 | /* Cache the time variables */ | ||
893 | state->time.max_render_time_in_us = | ||
894 | max_frame_duration_in_ns / 1000; | ||
895 | state->time.min_render_time_in_us = | ||
896 | min_frame_duration_in_ns / 1000; | ||
897 | state->btr.mid_point_in_us = | ||
898 | (max_frame_duration_in_ns + | ||
899 | min_frame_duration_in_ns) / 2000; | ||
900 | |||
901 | } | ||
902 | } | ||
903 | |||
904 | /* Program freesync according to current state*/ | ||
905 | set_freesync_on_streams(core_freesync, streams, num_streams); | ||
906 | } | ||
907 | |||
908 | /* Add the timestamps to the cache and determine whether BTR programming | ||
909 | * is required, depending on the times calculated | ||
910 | */ | ||
911 | static void update_timestamps(struct core_freesync *core_freesync, | ||
912 | const struct dc_stream *stream, unsigned int map_index, | ||
913 | unsigned int last_render_time_in_us) | ||
914 | { | ||
915 | struct freesync_state *state = &core_freesync->map[map_index].state; | ||
916 | |||
917 | state->time.render_times[state->time.render_times_index] = | ||
918 | last_render_time_in_us; | ||
919 | state->time.render_times_index++; | ||
920 | |||
921 | if (state->time.render_times_index >= RENDER_TIMES_MAX_COUNT) | ||
922 | state->time.render_times_index = 0; | ||
923 | |||
924 | if (last_render_time_in_us + BTR_EXIT_MARGIN < | ||
925 | state->time.max_render_time_in_us) { | ||
926 | |||
927 | /* Exit Below the Range */ | ||
928 | if (state->btr.btr_active) { | ||
929 | |||
930 | state->btr.program_btr = true; | ||
931 | state->btr.btr_active = false; | ||
932 | state->btr.frame_counter = 0; | ||
933 | |||
934 | /* Exit Fixed Refresh mode */ | ||
935 | } else if (state->fixed_refresh.fixed_refresh_active) { | ||
936 | |||
937 | state->fixed_refresh.program_fixed_refresh = true; | ||
938 | state->fixed_refresh.fixed_refresh_active = false; | ||
939 | |||
940 | } | ||
941 | |||
942 | } else if (last_render_time_in_us > state->time.max_render_time_in_us) { | ||
943 | |||
944 | /* Enter Below the Range */ | ||
945 | if (!state->btr.btr_active && | ||
946 | core_freesync->map[map_index].caps->btr_supported) { | ||
947 | |||
948 | state->btr.program_btr = true; | ||
949 | state->btr.btr_active = true; | ||
950 | |||
951 | /* Enter Fixed Refresh mode */ | ||
952 | } else if (!state->fixed_refresh.fixed_refresh_active && | ||
953 | !core_freesync->map[map_index].caps->btr_supported) { | ||
954 | |||
955 | state->fixed_refresh.program_fixed_refresh = true; | ||
956 | state->fixed_refresh.fixed_refresh_active = true; | ||
957 | |||
958 | } | ||
959 | } | ||
960 | |||
961 | /* When Below the Range is active, must react on every frame */ | ||
962 | if (state->btr.btr_active) | ||
963 | state->btr.program_btr = true; | ||
964 | } | ||
965 | |||
966 | static void apply_below_the_range(struct core_freesync *core_freesync, | ||
967 | const struct dc_stream *stream, unsigned int map_index, | ||
968 | unsigned int last_render_time_in_us) | ||
969 | { | ||
970 | unsigned int inserted_frame_duration_in_us = 0; | ||
971 | unsigned int mid_point_frames_ceil = 0; | ||
972 | unsigned int mid_point_frames_floor = 0; | ||
973 | unsigned int frame_time_in_us = 0; | ||
974 | unsigned int delta_from_mid_point_in_us_1 = 0xFFFFFFFF; | ||
975 | unsigned int delta_from_mid_point_in_us_2 = 0xFFFFFFFF; | ||
976 | unsigned int frames_to_insert = 0; | ||
977 | unsigned int inserted_frame_v_total = 0; | ||
978 | unsigned int vmin = 0, vmax = 0; | ||
979 | unsigned int min_frame_duration_in_ns = 0; | ||
980 | struct freesync_state *state = &core_freesync->map[map_index].state; | ||
981 | |||
982 | if (!state->btr.program_btr) | ||
983 | return; | ||
984 | |||
985 | state->btr.program_btr = false; | ||
986 | |||
987 | min_frame_duration_in_ns = ((unsigned int) (div64_u64( | ||
988 | (1000000000ULL * 1000000), | ||
989 | state->nominal_refresh_rate_in_micro_hz))); | ||
990 | |||
991 | /* Program BTR */ | ||
992 | |||
993 | /* BTR set to "not active" so disengage */ | ||
994 | if (!state->btr.btr_active) | ||
995 | |||
996 | /* Restore FreeSync */ | ||
997 | set_freesync_on_streams(core_freesync, &stream, 1); | ||
998 | |||
999 | /* BTR set to "active" so engage */ | ||
1000 | else { | ||
1001 | |||
1002 | /* Calculate number of midPoint frames that could fit within | ||
1003 | * the render time interval- take ceil of this value | ||
1004 | */ | ||
1005 | mid_point_frames_ceil = (last_render_time_in_us + | ||
1006 | state->btr.mid_point_in_us- 1) / | ||
1007 | state->btr.mid_point_in_us; | ||
1008 | |||
1009 | if (mid_point_frames_ceil > 0) { | ||
1010 | |||
1011 | frame_time_in_us = last_render_time_in_us / | ||
1012 | mid_point_frames_ceil; | ||
1013 | delta_from_mid_point_in_us_1 = (state->btr.mid_point_in_us > | ||
1014 | frame_time_in_us) ? | ||
1015 | (state->btr.mid_point_in_us - frame_time_in_us): | ||
1016 | (frame_time_in_us - state->btr.mid_point_in_us); | ||
1017 | } | ||
1018 | |||
1019 | /* Calculate number of midPoint frames that could fit within | ||
1020 | * the render time interval- take floor of this value | ||
1021 | */ | ||
1022 | mid_point_frames_floor = last_render_time_in_us / | ||
1023 | state->btr.mid_point_in_us; | ||
1024 | |||
1025 | if (mid_point_frames_floor > 0) { | ||
1026 | |||
1027 | frame_time_in_us = last_render_time_in_us / | ||
1028 | mid_point_frames_floor; | ||
1029 | delta_from_mid_point_in_us_2 = (state->btr.mid_point_in_us > | ||
1030 | frame_time_in_us) ? | ||
1031 | (state->btr.mid_point_in_us - frame_time_in_us): | ||
1032 | (frame_time_in_us - state->btr.mid_point_in_us); | ||
1033 | } | ||
1034 | |||
1035 | /* Choose number of frames to insert based on how close it | ||
1036 | * can get to the mid point of the variable range. | ||
1037 | */ | ||
1038 | if (delta_from_mid_point_in_us_1 < delta_from_mid_point_in_us_2) | ||
1039 | frames_to_insert = mid_point_frames_ceil; | ||
1040 | else | ||
1041 | frames_to_insert = mid_point_frames_floor; | ||
1042 | |||
1043 | /* Either we've calculated the number of frames to insert, | ||
1044 | * or we need to insert min duration frames | ||
1045 | */ | ||
1046 | if (frames_to_insert > 0) | ||
1047 | inserted_frame_duration_in_us = last_render_time_in_us / | ||
1048 | frames_to_insert; | ||
1049 | |||
1050 | if (inserted_frame_duration_in_us < | ||
1051 | state->time.min_render_time_in_us) | ||
1052 | |||
1053 | inserted_frame_duration_in_us = | ||
1054 | state->time.min_render_time_in_us; | ||
1055 | |||
1056 | /* We need the v_total_min from capability */ | ||
1057 | calc_vmin_vmax(core_freesync, stream, &vmin, &vmax); | ||
1058 | |||
1059 | inserted_frame_v_total = vmin; | ||
1060 | if (min_frame_duration_in_ns / 1000) | ||
1061 | inserted_frame_v_total = inserted_frame_duration_in_us * | ||
1062 | vmin / (min_frame_duration_in_ns / 1000); | ||
1063 | |||
1064 | /* Set length of inserted frames as v_total_max*/ | ||
1065 | vmax = inserted_frame_v_total; | ||
1066 | |||
1067 | /* Program V_TOTAL */ | ||
1068 | core_freesync->dc->stream_funcs.adjust_vmin_vmax( | ||
1069 | core_freesync->dc, &stream, | ||
1070 | 1, vmin, | ||
1071 | vmax); | ||
1072 | |||
1073 | /* Cache the calculated variables */ | ||
1074 | state->btr.inserted_frame_duration_in_us = | ||
1075 | inserted_frame_duration_in_us; | ||
1076 | state->btr.frames_to_insert = frames_to_insert; | ||
1077 | state->btr.frame_counter = frames_to_insert; | ||
1078 | |||
1079 | } | ||
1080 | } | ||
1081 | |||
1082 | static void apply_fixed_refresh(struct core_freesync *core_freesync, | ||
1083 | const struct dc_stream *stream, unsigned int map_index) | ||
1084 | { | ||
1085 | unsigned int vmin = 0, vmax = 0; | ||
1086 | struct freesync_state *state = &core_freesync->map[map_index].state; | ||
1087 | |||
1088 | if (!state->fixed_refresh.program_fixed_refresh) | ||
1089 | return; | ||
1090 | |||
1091 | state->fixed_refresh.program_fixed_refresh = false; | ||
1092 | |||
1093 | /* Program Fixed Refresh */ | ||
1094 | |||
1095 | /* Fixed Refresh set to "not active" so disengage */ | ||
1096 | if (!state->fixed_refresh.fixed_refresh_active) { | ||
1097 | set_freesync_on_streams(core_freesync, &stream, 1); | ||
1098 | |||
1099 | /* Fixed Refresh set to "active" so engage (fix to max) */ | ||
1100 | } else { | ||
1101 | |||
1102 | calc_vmin_vmax(core_freesync, stream, &vmin, &vmax); | ||
1103 | |||
1104 | vmax = vmin; | ||
1105 | |||
1106 | core_freesync->dc->stream_funcs.adjust_vmin_vmax( | ||
1107 | core_freesync->dc, &stream, | ||
1108 | 1, vmin, | ||
1109 | vmax); | ||
1110 | } | ||
1111 | } | ||
1112 | |||
1113 | void mod_freesync_pre_update_plane_addresses(struct mod_freesync *mod_freesync, | ||
1114 | const struct dc_stream **streams, int num_streams, | ||
1115 | unsigned int curr_time_stamp_in_us) | ||
1116 | { | ||
1117 | unsigned int stream_index, map_index, last_render_time_in_us = 0; | ||
1118 | struct core_freesync *core_freesync = | ||
1119 | MOD_FREESYNC_TO_CORE(mod_freesync); | ||
1120 | |||
1121 | for (stream_index = 0; stream_index < num_streams; stream_index++) { | ||
1122 | |||
1123 | map_index = map_index_from_stream(core_freesync, | ||
1124 | streams[stream_index]); | ||
1125 | |||
1126 | if (core_freesync->map[map_index].caps->supported) { | ||
1127 | |||
1128 | last_render_time_in_us = curr_time_stamp_in_us - | ||
1129 | core_freesync->map[map_index].state.time. | ||
1130 | prev_time_stamp_in_us; | ||
1131 | |||
1132 | /* Add the timestamps to the cache and determine | ||
1133 | * whether BTR program is required | ||
1134 | */ | ||
1135 | update_timestamps(core_freesync, streams[stream_index], | ||
1136 | map_index, last_render_time_in_us); | ||
1137 | |||
1138 | if (core_freesync->map[map_index].state.fullscreen && | ||
1139 | core_freesync->map[map_index].user_enable. | ||
1140 | enable_for_gaming) { | ||
1141 | |||
1142 | if (core_freesync->map[map_index].caps->btr_supported) { | ||
1143 | |||
1144 | apply_below_the_range(core_freesync, | ||
1145 | streams[stream_index], map_index, | ||
1146 | last_render_time_in_us); | ||
1147 | } else { | ||
1148 | apply_fixed_refresh(core_freesync, | ||
1149 | streams[stream_index], map_index); | ||
1150 | } | ||
1151 | } | ||
1152 | |||
1153 | core_freesync->map[map_index].state.time. | ||
1154 | prev_time_stamp_in_us = curr_time_stamp_in_us; | ||
1155 | } | ||
1156 | |||
1157 | } | ||
1158 | } | ||
diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_color.h b/drivers/gpu/drm/amd/display/modules/inc/mod_color.h new file mode 100644 index 000000000000..e54fe2cb8611 --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/inc/mod_color.h | |||
@@ -0,0 +1,179 @@ | |||
1 | /* | ||
2 | * Copyright 2016 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 | |||
27 | #ifndef MOD_COLOR_H_ | ||
28 | #define MOD_COLOR_H_ | ||
29 | |||
30 | #include "dm_services.h" | ||
31 | |||
32 | struct mod_color { | ||
33 | int dummy; | ||
34 | }; | ||
35 | |||
36 | struct color_space_coordinates { | ||
37 | unsigned int redX; | ||
38 | unsigned int redY; | ||
39 | unsigned int greenX; | ||
40 | unsigned int greenY; | ||
41 | unsigned int blueX; | ||
42 | unsigned int blueY; | ||
43 | unsigned int whiteX; | ||
44 | unsigned int whiteY; | ||
45 | }; | ||
46 | |||
47 | struct gamut_space_coordinates { | ||
48 | unsigned int redX; | ||
49 | unsigned int redY; | ||
50 | unsigned int greenX; | ||
51 | unsigned int greenY; | ||
52 | unsigned int blueX; | ||
53 | unsigned int blueY; | ||
54 | }; | ||
55 | |||
56 | struct gamut_space_entry { | ||
57 | unsigned int index; | ||
58 | unsigned int redX; | ||
59 | unsigned int redY; | ||
60 | unsigned int greenX; | ||
61 | unsigned int greenY; | ||
62 | unsigned int blueX; | ||
63 | unsigned int blueY; | ||
64 | |||
65 | int a0; | ||
66 | int a1; | ||
67 | int a2; | ||
68 | int a3; | ||
69 | int gamma; | ||
70 | }; | ||
71 | |||
72 | struct white_point_coodinates { | ||
73 | unsigned int whiteX; | ||
74 | unsigned int whiteY; | ||
75 | }; | ||
76 | |||
77 | struct white_point_coodinates_entry { | ||
78 | unsigned int index; | ||
79 | unsigned int whiteX; | ||
80 | unsigned int whiteY; | ||
81 | }; | ||
82 | |||
83 | struct color_range { | ||
84 | int current; | ||
85 | int min; | ||
86 | int max; | ||
87 | }; | ||
88 | |||
89 | struct mod_color *mod_color_create(struct dc *dc); | ||
90 | |||
91 | void mod_color_destroy(struct mod_color *mod_color); | ||
92 | |||
93 | bool mod_color_add_sink(struct mod_color *mod_color, | ||
94 | const struct dc_sink *sink); | ||
95 | |||
96 | bool mod_color_remove_sink(struct mod_color *mod_color, | ||
97 | const struct dc_sink *sink); | ||
98 | |||
99 | bool mod_color_update_gamut_to_stream(struct mod_color *mod_color, | ||
100 | const struct dc_stream **streams, int num_streams); | ||
101 | |||
102 | bool mod_color_set_white_point(struct mod_color *mod_color, | ||
103 | const struct dc_stream **streams, int num_streams, | ||
104 | struct white_point_coodinates *white_point); | ||
105 | |||
106 | bool mod_color_adjust_source_gamut(struct mod_color *mod_color, | ||
107 | const struct dc_stream **streams, int num_streams, | ||
108 | struct gamut_space_coordinates *input_gamut_coordinates, | ||
109 | struct white_point_coodinates *input_white_point_coordinates); | ||
110 | |||
111 | bool mod_color_adjust_destination_gamut(struct mod_color *mod_color, | ||
112 | const struct dc_stream **streams, int num_streams, | ||
113 | struct gamut_space_coordinates *input_gamut_coordinates, | ||
114 | struct white_point_coodinates *input_white_point_coordinates); | ||
115 | |||
116 | bool mod_color_get_user_enable(struct mod_color *mod_color, | ||
117 | const struct dc_sink *sink, | ||
118 | bool *user_enable); | ||
119 | |||
120 | bool mod_color_set_user_enable(struct mod_color *mod_color, | ||
121 | const struct dc_stream **streams, int num_streams, | ||
122 | bool user_enable); | ||
123 | |||
124 | bool mod_color_get_custom_color_temperature(struct mod_color *mod_color, | ||
125 | const struct dc_sink *sink, | ||
126 | int *color_temperature); | ||
127 | |||
128 | bool mod_color_set_custom_color_temperature(struct mod_color *mod_color, | ||
129 | const struct dc_stream **streams, int num_streams, | ||
130 | int color_temperature); | ||
131 | |||
132 | bool mod_color_get_color_saturation(struct mod_color *mod_color, | ||
133 | const struct dc_sink *sink, | ||
134 | struct color_range *color_saturation); | ||
135 | |||
136 | bool mod_color_get_color_contrast(struct mod_color *mod_color, | ||
137 | const struct dc_sink *sink, | ||
138 | struct color_range *color_contrast); | ||
139 | |||
140 | bool mod_color_get_color_brightness(struct mod_color *mod_color, | ||
141 | const struct dc_sink *sink, | ||
142 | struct color_range *color_brightness); | ||
143 | |||
144 | bool mod_color_get_color_hue(struct mod_color *mod_color, | ||
145 | const struct dc_sink *sink, | ||
146 | struct color_range *color_hue); | ||
147 | |||
148 | bool mod_color_get_source_gamut(struct mod_color *mod_color, | ||
149 | const struct dc_sink *sink, | ||
150 | struct color_space_coordinates *source_gamut); | ||
151 | |||
152 | bool mod_color_notify_mode_change(struct mod_color *mod_color, | ||
153 | const struct dc_stream **streams, int num_streams); | ||
154 | |||
155 | bool mod_color_set_brightness(struct mod_color *mod_color, | ||
156 | const struct dc_stream **streams, int num_streams, | ||
157 | int brightness_value); | ||
158 | |||
159 | bool mod_color_set_contrast(struct mod_color *mod_color, | ||
160 | const struct dc_stream **streams, int num_streams, | ||
161 | int contrast_value); | ||
162 | |||
163 | bool mod_color_set_hue(struct mod_color *mod_color, | ||
164 | const struct dc_stream **streams, int num_streams, | ||
165 | int hue_value); | ||
166 | |||
167 | bool mod_color_set_saturation(struct mod_color *mod_color, | ||
168 | const struct dc_stream **streams, int num_streams, | ||
169 | int saturation_value); | ||
170 | |||
171 | bool mod_color_set_preferred_quantization_range(struct mod_color *mod_color, | ||
172 | const struct dc_sink *sink, | ||
173 | enum dc_quantization_range quantization_range); | ||
174 | |||
175 | bool mod_color_get_preferred_quantization_range(struct mod_color *mod_color, | ||
176 | const struct dc_sink *sink, | ||
177 | enum dc_quantization_range *quantization_range); | ||
178 | |||
179 | #endif /* MOD_COLOR_H_ */ | ||
diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h b/drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h new file mode 100644 index 000000000000..7abfe34dc2d9 --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h | |||
@@ -0,0 +1,149 @@ | |||
1 | /* | ||
2 | * Copyright 2016 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 | |||
27 | |||
28 | |||
29 | /* | ||
30 | * Copyright 2016 Advanced Micro Devices, Inc. | ||
31 | * | ||
32 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
33 | * copy of this software and associated documentation files (the "Software"), | ||
34 | * to deal in the Software without restriction, including without limitation | ||
35 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
36 | * and/or sell copies of the Software, and to permit persons to whom the | ||
37 | * Software is furnished to do so, subject to the following conditions: | ||
38 | * | ||
39 | * The above copyright notice and this permission notice shall be included in | ||
40 | * all copies or substantial portions of the Software. | ||
41 | * | ||
42 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
43 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
44 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
45 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
46 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
47 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
48 | * OTHER DEALINGS IN THE SOFTWARE. | ||
49 | * | ||
50 | * Authors: AMD | ||
51 | * | ||
52 | */ | ||
53 | |||
54 | #ifndef MOD_FREESYNC_H_ | ||
55 | #define MOD_FREESYNC_H_ | ||
56 | |||
57 | #include "dm_services.h" | ||
58 | |||
59 | struct mod_freesync *mod_freesync_create(struct dc *dc); | ||
60 | void mod_freesync_destroy(struct mod_freesync *mod_freesync); | ||
61 | |||
62 | struct mod_freesync { | ||
63 | int dummy; | ||
64 | }; | ||
65 | |||
66 | enum mod_freesync_state { | ||
67 | FREESYNC_STATE_NONE, | ||
68 | FREESYNC_STATE_FULLSCREEN, | ||
69 | FREESYNC_STATE_STATIC_SCREEN, | ||
70 | FREESYNC_STATE_VIDEO | ||
71 | }; | ||
72 | |||
73 | enum mod_freesync_user_enable_mask { | ||
74 | FREESYNC_USER_ENABLE_STATIC = 0x1, | ||
75 | FREESYNC_USER_ENABLE_VIDEO = 0x2, | ||
76 | FREESYNC_USER_ENABLE_GAMING = 0x4 | ||
77 | }; | ||
78 | |||
79 | struct mod_freesync_user_enable { | ||
80 | bool enable_for_static; | ||
81 | bool enable_for_video; | ||
82 | bool enable_for_gaming; | ||
83 | }; | ||
84 | |||
85 | struct mod_freesync_caps { | ||
86 | bool supported; | ||
87 | unsigned int min_refresh_in_micro_hz; | ||
88 | unsigned int max_refresh_in_micro_hz; | ||
89 | |||
90 | bool btr_supported; | ||
91 | }; | ||
92 | |||
93 | struct mod_freesync_params { | ||
94 | enum mod_freesync_state state; | ||
95 | bool enable; | ||
96 | unsigned int update_duration_in_ns; | ||
97 | bool windowed_fullscreen; | ||
98 | }; | ||
99 | |||
100 | /* | ||
101 | * Add stream to be tracked by module | ||
102 | */ | ||
103 | bool mod_freesync_add_stream(struct mod_freesync *mod_freesync, | ||
104 | const struct dc_stream *stream, struct mod_freesync_caps *caps); | ||
105 | |||
106 | /* | ||
107 | * Remove stream to be tracked by module | ||
108 | */ | ||
109 | bool mod_freesync_remove_stream(struct mod_freesync *mod_freesync, | ||
110 | const struct dc_stream *stream); | ||
111 | |||
112 | /* | ||
113 | * Build additional parameters for dc_stream when creating stream for | ||
114 | * sink to support freesync | ||
115 | */ | ||
116 | void mod_freesync_update_stream(struct mod_freesync *mod_freesync, | ||
117 | struct dc_stream *stream); | ||
118 | |||
119 | /* | ||
120 | * Update the freesync state flags for each display and program | ||
121 | * freesync accordingly | ||
122 | */ | ||
123 | void mod_freesync_update_state(struct mod_freesync *mod_freesync, | ||
124 | const struct dc_stream **streams, int num_streams, | ||
125 | struct mod_freesync_params *freesync_params); | ||
126 | |||
127 | bool mod_freesync_get_state(struct mod_freesync *mod_freesync, | ||
128 | const struct dc_stream *stream, | ||
129 | struct mod_freesync_params *freesync_params); | ||
130 | |||
131 | bool mod_freesync_set_user_enable(struct mod_freesync *mod_freesync, | ||
132 | const struct dc_stream **streams, int num_streams, | ||
133 | struct mod_freesync_user_enable *user_enable); | ||
134 | |||
135 | bool mod_freesync_get_user_enable(struct mod_freesync *mod_freesync, | ||
136 | const struct dc_stream *stream, | ||
137 | struct mod_freesync_user_enable *user_enable); | ||
138 | |||
139 | void mod_freesync_handle_v_update(struct mod_freesync *mod_freesync, | ||
140 | const struct dc_stream **streams, int num_streams); | ||
141 | |||
142 | void mod_freesync_notify_mode_change(struct mod_freesync *mod_freesync, | ||
143 | const struct dc_stream **streams, int num_streams); | ||
144 | |||
145 | void mod_freesync_pre_update_plane_addresses(struct mod_freesync *mod_freesync, | ||
146 | const struct dc_stream **streams, int num_streams, | ||
147 | unsigned int curr_time_stamp); | ||
148 | |||
149 | #endif | ||
diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_power.h b/drivers/gpu/drm/amd/display/modules/inc/mod_power.h new file mode 100644 index 000000000000..a204e8d6cd23 --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/inc/mod_power.h | |||
@@ -0,0 +1,112 @@ | |||
1 | /* | ||
2 | * Copyright 2016 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 | #ifndef MODULES_INC_MOD_POWER_H_ | ||
27 | #define MODULES_INC_MOD_POWER_H_ | ||
28 | |||
29 | #include "dm_services.h" | ||
30 | |||
31 | struct mod_power { | ||
32 | int dummy; | ||
33 | }; | ||
34 | |||
35 | /* VariBright related commands */ | ||
36 | enum varibright_command { | ||
37 | VariBright_Cmd__SetVBLevel = 0, | ||
38 | VariBright_Cmd__UserEnable, | ||
39 | VariBright_Cmd__PreDisplayConfigChange, | ||
40 | VariBright_Cmd__PostDisplayConfigChange, | ||
41 | VariBright_Cmd__SuspendABM, | ||
42 | VariBright_Cmd__ResumeABM, | ||
43 | |||
44 | VariBright_Cmd__Unknown, | ||
45 | }; | ||
46 | |||
47 | /* VariBright settings structure */ | ||
48 | struct varibright_info { | ||
49 | enum varibright_command cmd; | ||
50 | |||
51 | unsigned int level; | ||
52 | bool enable; | ||
53 | bool activate; | ||
54 | }; | ||
55 | |||
56 | enum dmcu_block_psr_reason { | ||
57 | /* This is a bitfield mask */ | ||
58 | dmcu_block_psr_reason_invalid = 0x0, | ||
59 | dmcu_block_psr_reason_vsync_int = 0x1, | ||
60 | dmcu_block_psr_reason_shared_primary = 0x2, | ||
61 | dmcu_block_psr_reason_unsupported_link_rate = 0x4 | ||
62 | }; | ||
63 | |||
64 | struct mod_power *mod_power_create(struct dc *dc); | ||
65 | |||
66 | void mod_power_destroy(struct mod_power *mod_power); | ||
67 | |||
68 | bool mod_power_add_sink(struct mod_power *mod_power, | ||
69 | const struct dc_sink *sink); | ||
70 | |||
71 | bool mod_power_remove_sink(struct mod_power *mod_power, | ||
72 | const struct dc_sink *sink); | ||
73 | |||
74 | bool mod_power_set_backlight(struct mod_power *mod_power, | ||
75 | const struct dc_stream **streams, int num_streams, | ||
76 | unsigned int backlight_8bit); | ||
77 | |||
78 | bool mod_power_get_backlight(struct mod_power *mod_power, | ||
79 | const struct dc_sink *sink, | ||
80 | unsigned int *backlight_8bit); | ||
81 | |||
82 | void mod_power_initialize_backlight_caps | ||
83 | (struct mod_power *mod_power); | ||
84 | |||
85 | unsigned int mod_power_backlight_level_percentage_to_signal | ||
86 | (struct mod_power *mod_power, unsigned int percentage); | ||
87 | |||
88 | unsigned int mod_power_backlight_level_signal_to_percentage | ||
89 | (struct mod_power *mod_power, unsigned int signalLevel8bit); | ||
90 | |||
91 | bool mod_power_get_panel_backlight_boundaries | ||
92 | (struct mod_power *mod_power, | ||
93 | unsigned int *min_backlight, | ||
94 | unsigned int *max_backlight, | ||
95 | unsigned int *output_ac_level_percentage, | ||
96 | unsigned int *output_dc_level_percentage); | ||
97 | |||
98 | bool mod_power_set_smooth_brightness(struct mod_power *mod_power, | ||
99 | const struct dc_sink *sink, bool enable_brightness); | ||
100 | |||
101 | bool mod_power_notify_mode_change(struct mod_power *mod_power, | ||
102 | const struct dc_stream *stream); | ||
103 | |||
104 | bool mod_power_varibright_control(struct mod_power *mod_power, | ||
105 | struct varibright_info *input_varibright_info); | ||
106 | |||
107 | bool mod_power_block_psr(bool block_enable, enum dmcu_block_psr_reason reason); | ||
108 | |||
109 | bool mod_power_set_psr_enable(struct mod_power *mod_power, | ||
110 | bool psr_enable); | ||
111 | |||
112 | #endif /* MODULES_INC_MOD_POWER_H_ */ | ||
diff --git a/drivers/gpu/drm/amd/display/modules/power/power.c b/drivers/gpu/drm/amd/display/modules/power/power.c new file mode 100644 index 000000000000..ea07e847da0a --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/power/power.c | |||
@@ -0,0 +1,784 @@ | |||
1 | /* | ||
2 | * Copyright 2016 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 "mod_power.h" | ||
27 | #include "dm_services.h" | ||
28 | #include "dc.h" | ||
29 | #include "core_types.h" | ||
30 | #include "core_dc.h" | ||
31 | |||
32 | #define MOD_POWER_MAX_CONCURRENT_SINKS 32 | ||
33 | #define SMOOTH_BRIGHTNESS_ADJUSTMENT_TIME_IN_MS 500 | ||
34 | |||
35 | struct sink_caps { | ||
36 | const struct dc_sink *sink; | ||
37 | }; | ||
38 | |||
39 | struct backlight_state { | ||
40 | unsigned int backlight; | ||
41 | unsigned int frame_ramp; | ||
42 | bool smooth_brightness_enabled; | ||
43 | }; | ||
44 | |||
45 | struct core_power { | ||
46 | struct mod_power public; | ||
47 | struct dc *dc; | ||
48 | int num_sinks; | ||
49 | struct sink_caps *caps; | ||
50 | struct backlight_state *state; | ||
51 | }; | ||
52 | |||
53 | union dmcu_abm_set_bl_params { | ||
54 | struct { | ||
55 | unsigned int gradual_change : 1; /* [0:0] */ | ||
56 | unsigned int reserved : 15; /* [15:1] */ | ||
57 | unsigned int frame_ramp : 16; /* [31:16] */ | ||
58 | } bits; | ||
59 | unsigned int u32All; | ||
60 | }; | ||
61 | |||
62 | /* Backlight cached properties */ | ||
63 | static unsigned int backlight_8bit_lut_array[101]; | ||
64 | static unsigned int ac_level_percentage; | ||
65 | static unsigned int dc_level_percentage; | ||
66 | static bool backlight_caps_valid; | ||
67 | /* we use lazy initialization of backlight capabilities cache */ | ||
68 | static bool backlight_caps_initialized; | ||
69 | /* AC/DC levels initialized later in separate context */ | ||
70 | static bool backlight_def_levels_valid; | ||
71 | |||
72 | /* ABM cached properties */ | ||
73 | static unsigned int abm_level; | ||
74 | static bool abm_user_enable; | ||
75 | static bool abm_active; | ||
76 | |||
77 | /*PSR cached properties*/ | ||
78 | static unsigned int block_psr; | ||
79 | |||
80 | /* Defines default backlight curve F(x) = A(x*x) + Bx + C. | ||
81 | * | ||
82 | * Backlight curve should always satisfy F(0) = min, F(100) = max, | ||
83 | * so polynom coefficients are: | ||
84 | * A is 0.0255 - B/100 - min/10000 - (255-max)/10000 = (max - min)/10000 - B/100 | ||
85 | * B is adjustable factor to modify the curve. | ||
86 | * Bigger B results in less concave curve. B range is [0..(max-min)/100] | ||
87 | * C is backlight minimum | ||
88 | */ | ||
89 | static const unsigned int backlight_curve_coeff_a_factor = 10000; | ||
90 | static const unsigned int backlight_curve_coeff_b = 100; | ||
91 | static const unsigned int backlight_curve_coeff_b_factor = 100; | ||
92 | |||
93 | /* Minimum and maximum backlight input signal levels */ | ||
94 | static const unsigned int default_min_backlight = 12; | ||
95 | static const unsigned int default_max_backlight = 255; | ||
96 | |||
97 | /* Other backlight constants */ | ||
98 | static const unsigned int absolute_backlight_max = 255; | ||
99 | |||
100 | #define MOD_POWER_TO_CORE(mod_power)\ | ||
101 | container_of(mod_power, struct core_power, public) | ||
102 | |||
103 | static bool check_dc_support(const struct dc *dc) | ||
104 | { | ||
105 | if (dc->stream_funcs.set_backlight == NULL) | ||
106 | return false; | ||
107 | |||
108 | return true; | ||
109 | } | ||
110 | |||
111 | /* Given a specific dc_sink* this function finds its equivalent | ||
112 | * on the dc_sink array and returns the corresponding index | ||
113 | */ | ||
114 | static unsigned int sink_index_from_sink(struct core_power *core_power, | ||
115 | const struct dc_sink *sink) | ||
116 | { | ||
117 | unsigned int index = 0; | ||
118 | |||
119 | for (index = 0; index < core_power->num_sinks; index++) | ||
120 | if (core_power->caps[index].sink == sink) | ||
121 | return index; | ||
122 | |||
123 | /* Could not find sink requested */ | ||
124 | ASSERT(false); | ||
125 | return index; | ||
126 | } | ||
127 | |||
128 | static unsigned int convertBL8to17(unsigned int backlight_8bit) | ||
129 | { | ||
130 | unsigned int temp_ulong = backlight_8bit * 0x10101; | ||
131 | unsigned char temp_uchar = | ||
132 | (unsigned char)(((temp_ulong & 0x80) >> 7) & 1); | ||
133 | |||
134 | temp_ulong = (temp_ulong >> 8) + temp_uchar; | ||
135 | |||
136 | return temp_ulong; | ||
137 | } | ||
138 | |||
139 | static uint16_t convertBL8to16(unsigned int backlight_8bit) | ||
140 | { | ||
141 | return (uint16_t)((backlight_8bit * 0x10101) >> 8); | ||
142 | } | ||
143 | |||
144 | /*This is used when OS wants to retrieve the current BL. | ||
145 | * We return the 8bit value to OS. | ||
146 | */ | ||
147 | static unsigned int convertBL17to8(unsigned int backlight_17bit) | ||
148 | { | ||
149 | if (backlight_17bit & 0x10000) | ||
150 | return default_max_backlight; | ||
151 | else | ||
152 | return (backlight_17bit >> 8); | ||
153 | } | ||
154 | |||
155 | struct mod_power *mod_power_create(struct dc *dc) | ||
156 | { | ||
157 | struct core_power *core_power = | ||
158 | dm_alloc(sizeof(struct core_power)); | ||
159 | |||
160 | struct core_dc *core_dc = DC_TO_CORE(dc); | ||
161 | |||
162 | int i = 0; | ||
163 | |||
164 | if (core_power == NULL) | ||
165 | goto fail_alloc_context; | ||
166 | |||
167 | core_power->caps = dm_alloc(sizeof(struct sink_caps) * | ||
168 | MOD_POWER_MAX_CONCURRENT_SINKS); | ||
169 | |||
170 | if (core_power->caps == NULL) | ||
171 | goto fail_alloc_caps; | ||
172 | |||
173 | for (i = 0; i < MOD_POWER_MAX_CONCURRENT_SINKS; i++) | ||
174 | core_power->caps[i].sink = NULL; | ||
175 | |||
176 | core_power->state = dm_alloc(sizeof(struct backlight_state) * | ||
177 | MOD_POWER_MAX_CONCURRENT_SINKS); | ||
178 | |||
179 | if (core_power->state == NULL) | ||
180 | goto fail_alloc_state; | ||
181 | |||
182 | core_power->num_sinks = 0; | ||
183 | backlight_caps_valid = false; | ||
184 | |||
185 | if (dc == NULL) | ||
186 | goto fail_construct; | ||
187 | |||
188 | core_power->dc = dc; | ||
189 | |||
190 | if (!check_dc_support(dc)) | ||
191 | goto fail_construct; | ||
192 | |||
193 | abm_user_enable = false; | ||
194 | abm_active = false; | ||
195 | |||
196 | return &core_power->public; | ||
197 | |||
198 | fail_construct: | ||
199 | dm_free(core_power->state); | ||
200 | |||
201 | fail_alloc_state: | ||
202 | dm_free(core_power->caps); | ||
203 | |||
204 | fail_alloc_caps: | ||
205 | dm_free(core_power); | ||
206 | |||
207 | fail_alloc_context: | ||
208 | return NULL; | ||
209 | } | ||
210 | |||
211 | |||
212 | void mod_power_destroy(struct mod_power *mod_power) | ||
213 | { | ||
214 | if (mod_power != NULL) { | ||
215 | int i; | ||
216 | struct core_power *core_power = | ||
217 | MOD_POWER_TO_CORE(mod_power); | ||
218 | |||
219 | dm_free(core_power->state); | ||
220 | |||
221 | for (i = 0; i < core_power->num_sinks; i++) | ||
222 | dc_sink_release(core_power->caps[i].sink); | ||
223 | |||
224 | dm_free(core_power->caps); | ||
225 | |||
226 | dm_free(core_power); | ||
227 | } | ||
228 | } | ||
229 | |||
230 | bool mod_power_add_sink(struct mod_power *mod_power, | ||
231 | const struct dc_sink *sink) | ||
232 | { | ||
233 | if (sink->sink_signal == SIGNAL_TYPE_VIRTUAL) | ||
234 | return false; | ||
235 | |||
236 | struct core_power *core_power = | ||
237 | MOD_POWER_TO_CORE(mod_power); | ||
238 | struct core_dc *core_dc = DC_TO_CORE(core_power->dc); | ||
239 | |||
240 | if (core_power->num_sinks < MOD_POWER_MAX_CONCURRENT_SINKS) { | ||
241 | dc_sink_retain(sink); | ||
242 | core_power->caps[core_power->num_sinks].sink = sink; | ||
243 | core_power->state[core_power->num_sinks]. | ||
244 | smooth_brightness_enabled = false; | ||
245 | core_power->state[core_power->num_sinks]. | ||
246 | backlight = 100; | ||
247 | core_power->num_sinks++; | ||
248 | return true; | ||
249 | } | ||
250 | |||
251 | return false; | ||
252 | } | ||
253 | |||
254 | bool mod_power_remove_sink(struct mod_power *mod_power, | ||
255 | const struct dc_sink *sink) | ||
256 | { | ||
257 | int i = 0, j = 0; | ||
258 | struct core_power *core_power = | ||
259 | MOD_POWER_TO_CORE(mod_power); | ||
260 | |||
261 | for (i = 0; i < core_power->num_sinks; i++) { | ||
262 | if (core_power->caps[i].sink == sink) { | ||
263 | /* To remove this sink, shift everything after down */ | ||
264 | for (j = i; j < core_power->num_sinks - 1; j++) { | ||
265 | core_power->caps[j].sink = | ||
266 | core_power->caps[j + 1].sink; | ||
267 | |||
268 | memcpy(&core_power->state[j], | ||
269 | &core_power->state[j + 1], | ||
270 | sizeof(struct backlight_state)); | ||
271 | } | ||
272 | core_power->num_sinks--; | ||
273 | dc_sink_release(sink); | ||
274 | return true; | ||
275 | } | ||
276 | } | ||
277 | return false; | ||
278 | } | ||
279 | |||
280 | bool mod_power_set_backlight(struct mod_power *mod_power, | ||
281 | const struct dc_stream **streams, int num_streams, | ||
282 | unsigned int backlight_8bit) | ||
283 | { | ||
284 | struct core_power *core_power = | ||
285 | MOD_POWER_TO_CORE(mod_power); | ||
286 | |||
287 | unsigned int frame_ramp = 0; | ||
288 | |||
289 | unsigned int stream_index, sink_index, vsync_rate_hz; | ||
290 | |||
291 | union dmcu_abm_set_bl_params params; | ||
292 | |||
293 | for (stream_index = 0; stream_index < num_streams; stream_index++) { | ||
294 | if (streams[stream_index]->sink->sink_signal == SIGNAL_TYPE_VIRTUAL) { | ||
295 | core_power->state[sink_index].backlight = 0; | ||
296 | core_power->state[sink_index].frame_ramp = 0; | ||
297 | core_power->state[sink_index].smooth_brightness_enabled = false; | ||
298 | continue; | ||
299 | } | ||
300 | |||
301 | sink_index = sink_index_from_sink(core_power, | ||
302 | streams[stream_index]->sink); | ||
303 | |||
304 | vsync_rate_hz = div64_u64(div64_u64((streams[stream_index]-> | ||
305 | timing.pix_clk_khz * 1000), | ||
306 | streams[stream_index]->timing.v_total), | ||
307 | streams[stream_index]->timing.h_total); | ||
308 | |||
309 | core_power->state[sink_index].backlight = backlight_8bit; | ||
310 | |||
311 | if (core_power->state[sink_index].smooth_brightness_enabled) | ||
312 | frame_ramp = ((vsync_rate_hz * | ||
313 | SMOOTH_BRIGHTNESS_ADJUSTMENT_TIME_IN_MS) + 500) | ||
314 | / 1000; | ||
315 | else | ||
316 | frame_ramp = 0; | ||
317 | |||
318 | core_power->state[sink_index].frame_ramp = frame_ramp; | ||
319 | } | ||
320 | |||
321 | params.u32All = 0; | ||
322 | params.bits.gradual_change = (frame_ramp > 0); | ||
323 | params.bits.frame_ramp = frame_ramp; | ||
324 | |||
325 | core_power->dc->stream_funcs.set_backlight | ||
326 | (core_power->dc, backlight_8bit, params.u32All, streams[0]); | ||
327 | |||
328 | return true; | ||
329 | } | ||
330 | |||
331 | bool mod_power_get_backlight(struct mod_power *mod_power, | ||
332 | const struct dc_sink *sink, | ||
333 | unsigned int *backlight_8bit) | ||
334 | { | ||
335 | if (sink->sink_signal == SIGNAL_TYPE_VIRTUAL) | ||
336 | return false; | ||
337 | |||
338 | struct core_power *core_power = | ||
339 | MOD_POWER_TO_CORE(mod_power); | ||
340 | |||
341 | unsigned int sink_index = sink_index_from_sink(core_power, sink); | ||
342 | |||
343 | *backlight_8bit = core_power->state[sink_index].backlight; | ||
344 | |||
345 | return true; | ||
346 | } | ||
347 | |||
348 | /* hard coded to default backlight curve. */ | ||
349 | void mod_power_initialize_backlight_caps(struct mod_power | ||
350 | *mod_power) | ||
351 | { | ||
352 | struct core_power *core_power = | ||
353 | MOD_POWER_TO_CORE(mod_power); | ||
354 | struct core_dc *core_dc = DC_TO_CORE(core_power->dc); | ||
355 | unsigned int i; | ||
356 | |||
357 | backlight_caps_initialized = true; | ||
358 | |||
359 | struct dm_acpi_atif_backlight_caps *pExtCaps = NULL; | ||
360 | bool customCurvePresent = false; | ||
361 | bool customMinMaxPresent = false; | ||
362 | bool customDefLevelsPresent = false; | ||
363 | |||
364 | /* Allocate memory for ATIF output | ||
365 | * (do not want to use 256 bytes on the stack) | ||
366 | */ | ||
367 | pExtCaps = (struct dm_acpi_atif_backlight_caps *) | ||
368 | (dm_alloc(sizeof(struct dm_acpi_atif_backlight_caps))); | ||
369 | if (pExtCaps == NULL) | ||
370 | return; | ||
371 | |||
372 | /* Retrieve ACPI extended brightness caps */ | ||
373 | if (dm_query_extended_brightness_caps | ||
374 | (core_dc->ctx, AcpiDisplayType_LCD1, pExtCaps)) { | ||
375 | ac_level_percentage = pExtCaps->acLevelPercentage; | ||
376 | dc_level_percentage = pExtCaps->dcLevelPercentage; | ||
377 | customMinMaxPresent = true; | ||
378 | customDefLevelsPresent = true; | ||
379 | customCurvePresent = (pExtCaps->numOfDataPoints > 0); | ||
380 | |||
381 | ASSERT(pExtCaps->numOfDataPoints <= 99); | ||
382 | } else { | ||
383 | dm_free(pExtCaps); | ||
384 | return; | ||
385 | } | ||
386 | |||
387 | if (customMinMaxPresent) | ||
388 | backlight_8bit_lut_array[0] = pExtCaps->minInputSignal; | ||
389 | else | ||
390 | backlight_8bit_lut_array[0] = default_min_backlight; | ||
391 | |||
392 | if (customMinMaxPresent) | ||
393 | backlight_8bit_lut_array[100] = pExtCaps->maxInputSignal; | ||
394 | else | ||
395 | backlight_8bit_lut_array[100] = default_max_backlight; | ||
396 | |||
397 | ASSERT(backlight_8bit_lut_array[100] <= absolute_backlight_max); | ||
398 | ASSERT(backlight_8bit_lut_array[0] <= | ||
399 | backlight_8bit_lut_array[100]); | ||
400 | |||
401 | /* Just to make sure we use valid values */ | ||
402 | if (backlight_8bit_lut_array[100] > absolute_backlight_max) | ||
403 | backlight_8bit_lut_array[100] = absolute_backlight_max; | ||
404 | if (backlight_8bit_lut_array[0] > backlight_8bit_lut_array[100]) { | ||
405 | unsigned int swap; | ||
406 | |||
407 | swap = backlight_8bit_lut_array[0]; | ||
408 | backlight_8bit_lut_array[0] = backlight_8bit_lut_array[100]; | ||
409 | backlight_8bit_lut_array[100] = swap; | ||
410 | } | ||
411 | |||
412 | /* Build backlight translation table for custom curve */ | ||
413 | if (customCurvePresent) { | ||
414 | unsigned int index = 1; | ||
415 | unsigned int numOfDataPoints = | ||
416 | (pExtCaps->numOfDataPoints <= 99 ? | ||
417 | pExtCaps->numOfDataPoints : 99); | ||
418 | |||
419 | /* Filling translation table from data points - | ||
420 | * between every two provided data points we | ||
421 | * lineary interpolate missing values | ||
422 | */ | ||
423 | for (i = 0; i < numOfDataPoints; i++) { | ||
424 | /* Clamp signal level between min and max | ||
425 | * (since min and max might come other | ||
426 | * soruce like registry) | ||
427 | */ | ||
428 | unsigned int luminance = | ||
429 | pExtCaps->dataPoints[i].luminance; | ||
430 | unsigned int signalLevel = | ||
431 | pExtCaps->dataPoints[i].signalLevel; | ||
432 | |||
433 | if (signalLevel < backlight_8bit_lut_array[0]) | ||
434 | signalLevel = backlight_8bit_lut_array[0]; | ||
435 | if (signalLevel > backlight_8bit_lut_array[100]) | ||
436 | signalLevel = backlight_8bit_lut_array[100]; | ||
437 | |||
438 | /* Lineary interpolate missing values */ | ||
439 | if (index < luminance) { | ||
440 | unsigned int baseValue = | ||
441 | backlight_8bit_lut_array[index-1]; | ||
442 | unsigned int deltaSignal = | ||
443 | signalLevel - baseValue; | ||
444 | unsigned int deltaLuma = | ||
445 | luminance - index + 1; | ||
446 | unsigned int step = deltaSignal; | ||
447 | |||
448 | for (; index < luminance; index++) { | ||
449 | backlight_8bit_lut_array[index] = | ||
450 | baseValue + (step / deltaLuma); | ||
451 | step += deltaSignal; | ||
452 | } | ||
453 | } | ||
454 | |||
455 | /* Now [index == luminance], | ||
456 | * so we can add data point to the translation table | ||
457 | */ | ||
458 | backlight_8bit_lut_array[index++] = signalLevel; | ||
459 | } | ||
460 | |||
461 | /* Complete the final segment of interpolation - | ||
462 | * between last datapoint and maximum value | ||
463 | */ | ||
464 | if (index < 100) { | ||
465 | unsigned int baseValue = | ||
466 | backlight_8bit_lut_array[index-1]; | ||
467 | unsigned int deltaSignal = | ||
468 | backlight_8bit_lut_array[100] - | ||
469 | baseValue; | ||
470 | unsigned int deltaLuma = 100 - index + 1; | ||
471 | unsigned int step = deltaSignal; | ||
472 | |||
473 | for (; index < 100; index++) { | ||
474 | backlight_8bit_lut_array[index] = | ||
475 | baseValue + (step / deltaLuma); | ||
476 | step += deltaSignal; | ||
477 | } | ||
478 | } | ||
479 | /* Build backlight translation table based on default curve */ | ||
480 | } else { | ||
481 | unsigned int delta = | ||
482 | backlight_8bit_lut_array[100] - | ||
483 | backlight_8bit_lut_array[0]; | ||
484 | unsigned int coeffC = backlight_8bit_lut_array[0]; | ||
485 | unsigned int coeffB = | ||
486 | (backlight_curve_coeff_b < delta ? | ||
487 | backlight_curve_coeff_b : delta); | ||
488 | unsigned int coeffA = delta - coeffB; /* coeffB is B*100 */ | ||
489 | |||
490 | for (i = 1; i < 100; i++) { | ||
491 | backlight_8bit_lut_array[i] = | ||
492 | (coeffA * i * i) / | ||
493 | backlight_curve_coeff_a_factor + | ||
494 | (coeffB * i) / | ||
495 | backlight_curve_coeff_b_factor + | ||
496 | coeffC; | ||
497 | } | ||
498 | } | ||
499 | |||
500 | if (pExtCaps != NULL) | ||
501 | dm_free(pExtCaps); | ||
502 | |||
503 | /* Successfully initialized */ | ||
504 | backlight_caps_valid = true; | ||
505 | backlight_def_levels_valid = customDefLevelsPresent; | ||
506 | } | ||
507 | |||
508 | unsigned int mod_power_backlight_level_percentage_to_signal( | ||
509 | struct mod_power *mod_power, unsigned int percentage) | ||
510 | { | ||
511 | /* Do lazy initialization of backlight capabilities*/ | ||
512 | if (!backlight_caps_initialized) | ||
513 | mod_power_initialize_backlight_caps(mod_power); | ||
514 | |||
515 | /* Since the translation table is indexed by percentage, | ||
516 | * we simply return backlight value at given percent | ||
517 | */ | ||
518 | if (backlight_caps_valid && percentage <= 100) | ||
519 | return backlight_8bit_lut_array[percentage]; | ||
520 | |||
521 | return -1; | ||
522 | } | ||
523 | |||
524 | unsigned int mod_power_backlight_level_signal_to_percentage( | ||
525 | struct mod_power *mod_power, | ||
526 | unsigned int signalLevel8bit) | ||
527 | { | ||
528 | unsigned int invalid_backlight = (unsigned int)(-1); | ||
529 | /* Do lazy initialization of backlight capabilities */ | ||
530 | if (!backlight_caps_initialized) | ||
531 | mod_power_initialize_backlight_caps(mod_power); | ||
532 | |||
533 | /* If customer curve cannot convert to differentiated value near min | ||
534 | * it is important to report 0 for min signal to pass setting "Dimmed" | ||
535 | * setting in HCK brightness2 tests. | ||
536 | */ | ||
537 | if (signalLevel8bit <= backlight_8bit_lut_array[0]) | ||
538 | return 0; | ||
539 | |||
540 | /* Since the translation table is indexed by percentage | ||
541 | * we need to do a binary search over the array | ||
542 | * Another option would be to guess entry based on linear distribution | ||
543 | * and then do linear search in correct direction | ||
544 | */ | ||
545 | if (backlight_caps_valid && signalLevel8bit <= | ||
546 | absolute_backlight_max) { | ||
547 | unsigned int min = 0; | ||
548 | unsigned int max = 100; | ||
549 | unsigned int mid = invalid_backlight; | ||
550 | |||
551 | while (max >= min) { | ||
552 | mid = (min + max) / 2; /* floor of half range */ | ||
553 | |||
554 | if (backlight_8bit_lut_array[mid] < signalLevel8bit) | ||
555 | min = mid + 1; | ||
556 | else if (backlight_8bit_lut_array[mid] > | ||
557 | signalLevel8bit) | ||
558 | max = mid - 1; | ||
559 | else | ||
560 | break; | ||
561 | |||
562 | if (max == 0 || max == 1) | ||
563 | return invalid_backlight; | ||
564 | } | ||
565 | return mid; | ||
566 | } | ||
567 | |||
568 | return invalid_backlight; | ||
569 | } | ||
570 | |||
571 | |||
572 | bool mod_power_get_panel_backlight_boundaries( | ||
573 | struct mod_power *mod_power, | ||
574 | unsigned int *min_backlight, | ||
575 | unsigned int *max_backlight, | ||
576 | unsigned int *output_ac_level_percentage, | ||
577 | unsigned int *output_dc_level_percentage) | ||
578 | { | ||
579 | /* Do lazy initialization of backlight capabilities */ | ||
580 | if (!backlight_caps_initialized) | ||
581 | mod_power_initialize_backlight_caps(mod_power); | ||
582 | |||
583 | /* If cache was successfully updated, | ||
584 | * copy the values to output structure and return success | ||
585 | */ | ||
586 | if (backlight_caps_valid) { | ||
587 | *min_backlight = backlight_8bit_lut_array[0]; | ||
588 | *max_backlight = backlight_8bit_lut_array[100]; | ||
589 | |||
590 | *output_ac_level_percentage = ac_level_percentage; | ||
591 | *output_dc_level_percentage = dc_level_percentage; | ||
592 | |||
593 | return true; | ||
594 | } | ||
595 | |||
596 | return false; | ||
597 | } | ||
598 | |||
599 | bool mod_power_set_smooth_brightness(struct mod_power *mod_power, | ||
600 | const struct dc_sink *sink, bool enable_brightness) | ||
601 | { | ||
602 | if (sink->sink_signal == SIGNAL_TYPE_VIRTUAL) | ||
603 | return false; | ||
604 | |||
605 | struct core_power *core_power = | ||
606 | MOD_POWER_TO_CORE(mod_power); | ||
607 | unsigned int sink_index = sink_index_from_sink(core_power, sink); | ||
608 | |||
609 | core_power->state[sink_index].smooth_brightness_enabled | ||
610 | = enable_brightness; | ||
611 | return true; | ||
612 | } | ||
613 | |||
614 | bool mod_power_notify_mode_change(struct mod_power *mod_power, | ||
615 | const struct dc_stream *stream) | ||
616 | { | ||
617 | if (stream->sink->sink_signal == SIGNAL_TYPE_VIRTUAL) | ||
618 | return false; | ||
619 | |||
620 | struct core_power *core_power = | ||
621 | MOD_POWER_TO_CORE(mod_power); | ||
622 | |||
623 | unsigned int sink_index = sink_index_from_sink(core_power, | ||
624 | stream->sink); | ||
625 | unsigned int frame_ramp = core_power->state[sink_index].frame_ramp; | ||
626 | union dmcu_abm_set_bl_params params; | ||
627 | |||
628 | params.u32All = 0; | ||
629 | params.bits.gradual_change = (frame_ramp > 0); | ||
630 | params.bits.frame_ramp = frame_ramp; | ||
631 | |||
632 | core_power->dc->stream_funcs.set_backlight | ||
633 | (core_power->dc, | ||
634 | core_power->state[sink_index].backlight, | ||
635 | params.u32All, stream); | ||
636 | |||
637 | core_power->dc->stream_funcs.setup_psr | ||
638 | (core_power->dc, stream); | ||
639 | |||
640 | return true; | ||
641 | } | ||
642 | |||
643 | |||
644 | static bool mod_power_abm_feature_enable(struct mod_power | ||
645 | *mod_power, bool enable) | ||
646 | { | ||
647 | struct core_power *core_power = | ||
648 | MOD_POWER_TO_CORE(mod_power); | ||
649 | if (abm_user_enable == enable) | ||
650 | return true; | ||
651 | |||
652 | abm_user_enable = enable; | ||
653 | |||
654 | if (enable) { | ||
655 | if (abm_level != 0 && abm_active) | ||
656 | core_power->dc->stream_funcs.set_abm_level | ||
657 | (core_power->dc, abm_level); | ||
658 | } else { | ||
659 | if (abm_level != 0 && abm_active) { | ||
660 | abm_level = 0; | ||
661 | core_power->dc->stream_funcs.set_abm_level | ||
662 | (core_power->dc, abm_level); | ||
663 | } | ||
664 | } | ||
665 | |||
666 | return true; | ||
667 | } | ||
668 | |||
669 | static bool mod_power_abm_activate(struct mod_power | ||
670 | *mod_power, bool activate) | ||
671 | { | ||
672 | struct core_power *core_power = | ||
673 | MOD_POWER_TO_CORE(mod_power); | ||
674 | if (abm_active == activate) | ||
675 | return true; | ||
676 | |||
677 | abm_active = activate; | ||
678 | |||
679 | if (activate) { | ||
680 | if (abm_level != 0 && abm_user_enable) | ||
681 | core_power->dc->stream_funcs.set_abm_level | ||
682 | (core_power->dc, abm_level); | ||
683 | } else { | ||
684 | if (abm_level != 0 && abm_user_enable) { | ||
685 | abm_level = 0; | ||
686 | core_power->dc->stream_funcs.set_abm_level | ||
687 | (core_power->dc, abm_level); | ||
688 | } | ||
689 | } | ||
690 | |||
691 | return true; | ||
692 | } | ||
693 | |||
694 | static bool mod_power_abm_set_level(struct mod_power *mod_power, | ||
695 | unsigned int level) | ||
696 | { | ||
697 | struct core_power *core_power = | ||
698 | MOD_POWER_TO_CORE(mod_power); | ||
699 | if (abm_level == level) | ||
700 | return true; | ||
701 | |||
702 | if (abm_active && abm_user_enable && level == 0) | ||
703 | core_power->dc->stream_funcs.set_abm_level | ||
704 | (core_power->dc, 0); | ||
705 | else if (abm_active && abm_user_enable && level != 0) | ||
706 | core_power->dc->stream_funcs.set_abm_level | ||
707 | (core_power->dc, level); | ||
708 | |||
709 | abm_level = level; | ||
710 | |||
711 | return true; | ||
712 | } | ||
713 | |||
714 | bool mod_power_varibright_control(struct mod_power *mod_power, | ||
715 | struct varibright_info *input_varibright_info) | ||
716 | { | ||
717 | switch (input_varibright_info->cmd) { | ||
718 | case VariBright_Cmd__SetVBLevel: | ||
719 | { | ||
720 | /* Set VariBright user level. */ | ||
721 | mod_power_abm_set_level(mod_power, | ||
722 | input_varibright_info->level); | ||
723 | } | ||
724 | break; | ||
725 | |||
726 | case VariBright_Cmd__UserEnable: | ||
727 | { | ||
728 | /* Set VariBright user enable state. */ | ||
729 | mod_power_abm_feature_enable(mod_power, | ||
730 | input_varibright_info->enable); | ||
731 | } | ||
732 | break; | ||
733 | |||
734 | case VariBright_Cmd__PostDisplayConfigChange: | ||
735 | { | ||
736 | /* Set VariBright user level. */ | ||
737 | mod_power_abm_set_level(mod_power, | ||
738 | input_varibright_info->level); | ||
739 | |||
740 | /* Set VariBright user enable state. */ | ||
741 | mod_power_abm_feature_enable(mod_power, | ||
742 | input_varibright_info->enable); | ||
743 | |||
744 | /* Set VariBright activate based on power state. */ | ||
745 | mod_power_abm_activate(mod_power, | ||
746 | input_varibright_info->activate); | ||
747 | } | ||
748 | break; | ||
749 | |||
750 | default: | ||
751 | { | ||
752 | return false; | ||
753 | } | ||
754 | break; | ||
755 | } | ||
756 | |||
757 | return true; | ||
758 | } | ||
759 | |||
760 | bool mod_power_block_psr(bool block_enable, enum dmcu_block_psr_reason reason) | ||
761 | { | ||
762 | if (block_enable) | ||
763 | block_psr |= reason; | ||
764 | else | ||
765 | block_psr &= ~reason; | ||
766 | |||
767 | return true; | ||
768 | } | ||
769 | |||
770 | |||
771 | bool mod_power_set_psr_enable(struct mod_power *mod_power, | ||
772 | bool psr_enable) | ||
773 | { | ||
774 | struct core_power *core_power = | ||
775 | MOD_POWER_TO_CORE(mod_power); | ||
776 | |||
777 | if (block_psr == 0) | ||
778 | return core_power->dc->stream_funcs.set_psr_enable | ||
779 | (core_power->dc, psr_enable); | ||
780 | |||
781 | return false; | ||
782 | } | ||
783 | |||
784 | |||