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