1 /* Copyright 2024 Advanced Micro Devices, Inc.
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining a
4 * copy of this software and associated documentation files (the "Software"),
5 * to deal in the Software without restriction, including without limitation
6 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 * and/or sell copies of the Software, and to permit persons to whom the
8 * Software is furnished to do so, subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
17 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
18 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
19 * OTHER DEALINGS IN THE SOFTWARE.
20 *
21 * Authors: AMD
22 *
23 */
24 #include "vpe_priv.h"
25 #include "reg_helper.h"
26 #include "vpe10/inc/vpe10_cm_common.h"
27 #include "vpe10_dpp.h"
28 #include "conversion.h"
29 #define CTX vpe10_dpp
30 #define CTX_BASE dpp
31
vpe10_enable_cm_block(struct dpp * dpp)32 static void vpe10_enable_cm_block(struct dpp *dpp)
33 {
34 unsigned int cm_bypass_mode = 0;
35
36 PROGRAM_ENTRY();
37
38 // debug option: put CM in bypass mode
39 if (vpe_priv->init.debug.cm_in_bypass)
40 cm_bypass_mode = 1;
41
42 REG_SET(VPCM_CONTROL, 0, VPCM_BYPASS, cm_bypass_mode);
43 }
44
vpe10_power_on_gamcor_lut(struct dpp * dpp,bool power_on)45 static void vpe10_power_on_gamcor_lut(struct dpp *dpp, bool power_on)
46 {
47 PROGRAM_ENTRY();
48
49 if (vpe_priv->init.debug.enable_mem_low_power.bits.cm) {
50 if (power_on) {
51 REG_SET_2(VPCM_MEM_PWR_CTRL, REG_DEFAULT(VPCM_MEM_PWR_CTRL), GAMCOR_MEM_PWR_DIS, 0,
52 GAMCOR_MEM_PWR_FORCE, 0);
53
54 // two dummy updates (10-15clks each) for wake up delay
55 REG_SET_2(VPCM_MEM_PWR_CTRL, REG_DEFAULT(VPCM_MEM_PWR_CTRL), GAMCOR_MEM_PWR_DIS, 0,
56 GAMCOR_MEM_PWR_FORCE, 0);
57 REG_SET_2(VPCM_MEM_PWR_CTRL, REG_DEFAULT(VPCM_MEM_PWR_CTRL), GAMCOR_MEM_PWR_DIS, 0,
58 GAMCOR_MEM_PWR_FORCE, 0);
59 } else {
60 REG_SET_2(VPCM_MEM_PWR_CTRL, REG_DEFAULT(VPCM_MEM_PWR_CTRL), GAMCOR_MEM_PWR_DIS, 0,
61 GAMCOR_MEM_PWR_FORCE, 3);
62 }
63 } else {
64 REG_SET_2(VPCM_MEM_PWR_CTRL, REG_DEFAULT(VPCM_MEM_PWR_CTRL), GAMCOR_MEM_PWR_DIS,
65 power_on == true ? 1 : 0, GAMCOR_MEM_PWR_FORCE, 0);
66 }
67 }
68
vpe10_configure_gamcor_lut(struct dpp * dpp)69 static void vpe10_configure_gamcor_lut(struct dpp *dpp)
70 {
71 PROGRAM_ENTRY();
72
73 REG_SET(VPCM_GAMCOR_LUT_CONTROL, 0, VPCM_GAMCOR_LUT_WRITE_COLOR_MASK, 7);
74 REG_SET(VPCM_GAMCOR_LUT_INDEX, 0, VPCM_GAMCOR_LUT_INDEX, 0);
75 }
76
vpe10_dpp_gamcor_reg_field(struct dpp * dpp,struct vpe10_xfer_func_reg * reg)77 static void vpe10_dpp_gamcor_reg_field(struct dpp *dpp, struct vpe10_xfer_func_reg *reg)
78 {
79 struct vpe10_dpp *vpe10_dpp = (struct vpe10_dpp *)dpp;
80
81 reg->shifts.field_region_start_base =
82 vpe10_dpp->shift->VPCM_GAMCOR_RAMA_EXP_REGION_START_BASE_B;
83 reg->masks.field_region_start_base = vpe10_dpp->mask->VPCM_GAMCOR_RAMA_EXP_REGION_START_BASE_B;
84 reg->shifts.field_offset = vpe10_dpp->shift->VPCM_GAMCOR_RAMA_OFFSET_B;
85 reg->masks.field_offset = vpe10_dpp->mask->VPCM_GAMCOR_RAMA_OFFSET_B;
86
87 reg->shifts.exp_region0_lut_offset = vpe10_dpp->shift->VPCM_GAMCOR_RAMA_EXP_REGION0_LUT_OFFSET;
88 reg->masks.exp_region0_lut_offset = vpe10_dpp->mask->VPCM_GAMCOR_RAMA_EXP_REGION0_LUT_OFFSET;
89 reg->shifts.exp_region0_num_segments =
90 vpe10_dpp->shift->VPCM_GAMCOR_RAMA_EXP_REGION0_NUM_SEGMENTS;
91 reg->masks.exp_region0_num_segments =
92 vpe10_dpp->mask->VPCM_GAMCOR_RAMA_EXP_REGION0_NUM_SEGMENTS;
93 reg->shifts.exp_region1_lut_offset = vpe10_dpp->shift->VPCM_GAMCOR_RAMA_EXP_REGION1_LUT_OFFSET;
94 reg->masks.exp_region1_lut_offset = vpe10_dpp->mask->VPCM_GAMCOR_RAMA_EXP_REGION1_LUT_OFFSET;
95 reg->shifts.exp_region1_num_segments =
96 vpe10_dpp->shift->VPCM_GAMCOR_RAMA_EXP_REGION1_NUM_SEGMENTS;
97 reg->masks.exp_region1_num_segments =
98 vpe10_dpp->mask->VPCM_GAMCOR_RAMA_EXP_REGION1_NUM_SEGMENTS;
99
100 reg->shifts.field_region_end = vpe10_dpp->shift->VPCM_GAMCOR_RAMA_EXP_REGION_END_B;
101 reg->masks.field_region_end = vpe10_dpp->mask->VPCM_GAMCOR_RAMA_EXP_REGION_END_B;
102 reg->shifts.field_region_end_slope = vpe10_dpp->shift->VPCM_GAMCOR_RAMA_EXP_REGION_END_SLOPE_B;
103 reg->masks.field_region_end_slope = vpe10_dpp->mask->VPCM_GAMCOR_RAMA_EXP_REGION_END_SLOPE_B;
104 reg->shifts.field_region_end_base = vpe10_dpp->shift->VPCM_GAMCOR_RAMA_EXP_REGION_END_BASE_B;
105 reg->masks.field_region_end_base = vpe10_dpp->mask->VPCM_GAMCOR_RAMA_EXP_REGION_END_BASE_B;
106 reg->shifts.field_region_linear_slope =
107 vpe10_dpp->shift->VPCM_GAMCOR_RAMA_EXP_REGION_START_SLOPE_B;
108 reg->masks.field_region_linear_slope =
109 vpe10_dpp->mask->VPCM_GAMCOR_RAMA_EXP_REGION_START_SLOPE_B;
110 reg->shifts.exp_region_start = vpe10_dpp->shift->VPCM_GAMCOR_RAMA_EXP_REGION_START_B;
111 reg->masks.exp_region_start = vpe10_dpp->mask->VPCM_GAMCOR_RAMA_EXP_REGION_START_B;
112 reg->shifts.exp_region_start_segment =
113 vpe10_dpp->shift->VPCM_GAMCOR_RAMA_EXP_REGION_START_SEGMENT_B;
114 reg->masks.exp_region_start_segment =
115 vpe10_dpp->mask->VPCM_GAMCOR_RAMA_EXP_REGION_START_SEGMENT_B;
116 }
117
vpe10_dpp_program_gammcor_lut(struct dpp * dpp,const struct pwl_result_data * rgb,uint32_t num)118 static void vpe10_dpp_program_gammcor_lut(
119 struct dpp *dpp, const struct pwl_result_data *rgb, uint32_t num)
120 {
121 uint32_t last_base_value_red = rgb[num].red_reg;
122 uint32_t last_base_value_green = rgb[num].blue_reg;
123 uint32_t last_base_value_blue = rgb[num].green_reg;
124
125 PROGRAM_ENTRY();
126
127 /*fill in the LUT with all base values to be used by pwl module
128 * HW auto increments the LUT index: back-to-back write
129 */
130 if (vpe_is_rgb_equal(rgb, num)) {
131 vpe10_cm_helper_program_pwl(config_writer, rgb, last_base_value_red, num,
132 REG_OFFSET(VPCM_GAMCOR_LUT_DATA), REG_FIELD_SHIFT(VPCM_GAMCOR_LUT_DATA),
133 REG_FIELD_MASK(VPCM_GAMCOR_LUT_DATA), CM_PWL_R);
134 } else {
135 REG_UPDATE(VPCM_GAMCOR_LUT_CONTROL, VPCM_GAMCOR_LUT_WRITE_COLOR_MASK, 4);
136
137 vpe10_cm_helper_program_pwl(config_writer, rgb, last_base_value_red, num,
138 REG_OFFSET(VPCM_GAMCOR_LUT_DATA), REG_FIELD_SHIFT(VPCM_GAMCOR_LUT_DATA),
139 REG_FIELD_MASK(VPCM_GAMCOR_LUT_DATA), CM_PWL_R);
140
141 REG_SET(VPCM_GAMCOR_LUT_INDEX, 0, VPCM_GAMCOR_LUT_INDEX, 0);
142 REG_UPDATE(VPCM_GAMCOR_LUT_CONTROL, VPCM_GAMCOR_LUT_WRITE_COLOR_MASK, 2);
143
144 vpe10_cm_helper_program_pwl(config_writer, rgb, last_base_value_green, num,
145 REG_OFFSET(VPCM_GAMCOR_LUT_DATA), REG_FIELD_SHIFT(VPCM_GAMCOR_LUT_DATA),
146 REG_FIELD_MASK(VPCM_GAMCOR_LUT_DATA), CM_PWL_G);
147
148 REG_SET(VPCM_GAMCOR_LUT_INDEX, 0, VPCM_GAMCOR_LUT_INDEX, 0);
149 REG_UPDATE(VPCM_GAMCOR_LUT_CONTROL, VPCM_GAMCOR_LUT_WRITE_COLOR_MASK, 1);
150
151 vpe10_cm_helper_program_pwl(config_writer, rgb, last_base_value_blue, num,
152 REG_OFFSET(VPCM_GAMCOR_LUT_DATA), REG_FIELD_SHIFT(VPCM_GAMCOR_LUT_DATA),
153 REG_FIELD_MASK(VPCM_GAMCOR_LUT_DATA), CM_PWL_B);
154 }
155 }
156
vpe10_dpp_program_gamcor_lut(struct dpp * dpp,const struct pwl_params * params)157 void vpe10_dpp_program_gamcor_lut(struct dpp *dpp, const struct pwl_params *params)
158 {
159 struct vpe10_xfer_func_reg gam_regs = {0};
160
161 PROGRAM_ENTRY();
162
163 vpe10_enable_cm_block(dpp);
164
165 if (dpp->vpe_priv->init.debug.bypass_gamcor || params == NULL) {
166 // bypass
167 REG_SET(VPCM_GAMCOR_CONTROL, 0, VPCM_GAMCOR_MODE, 0);
168 vpe10_power_on_gamcor_lut(dpp, false);
169 return;
170 }
171
172 vpe10_power_on_gamcor_lut(dpp, true);
173 vpe10_configure_gamcor_lut(dpp);
174
175 REG_SET(VPCM_GAMCOR_CONTROL, 0, VPCM_GAMCOR_MODE, 2); // programmable RAM
176
177 gam_regs.start_cntl_b = REG_OFFSET(VPCM_GAMCOR_RAMA_START_CNTL_B);
178 gam_regs.start_cntl_g = REG_OFFSET(VPCM_GAMCOR_RAMA_START_CNTL_G);
179 gam_regs.start_cntl_r = REG_OFFSET(VPCM_GAMCOR_RAMA_START_CNTL_R);
180 gam_regs.start_slope_cntl_b = REG_OFFSET(VPCM_GAMCOR_RAMA_START_SLOPE_CNTL_B);
181 gam_regs.start_slope_cntl_g = REG_OFFSET(VPCM_GAMCOR_RAMA_START_SLOPE_CNTL_G);
182 gam_regs.start_slope_cntl_r = REG_OFFSET(VPCM_GAMCOR_RAMA_START_SLOPE_CNTL_R);
183 gam_regs.start_end_cntl1_b = REG_OFFSET(VPCM_GAMCOR_RAMA_END_CNTL1_B);
184 gam_regs.start_end_cntl2_b = REG_OFFSET(VPCM_GAMCOR_RAMA_END_CNTL2_B);
185 gam_regs.start_end_cntl1_g = REG_OFFSET(VPCM_GAMCOR_RAMA_END_CNTL1_G);
186 gam_regs.start_end_cntl2_g = REG_OFFSET(VPCM_GAMCOR_RAMA_END_CNTL2_G);
187 gam_regs.start_end_cntl1_r = REG_OFFSET(VPCM_GAMCOR_RAMA_END_CNTL1_R);
188 gam_regs.start_end_cntl2_r = REG_OFFSET(VPCM_GAMCOR_RAMA_END_CNTL2_R);
189 gam_regs.region_start = REG_OFFSET(VPCM_GAMCOR_RAMA_REGION_0_1);
190 gam_regs.region_end = REG_OFFSET(VPCM_GAMCOR_RAMA_REGION_32_33);
191 gam_regs.offset_b = REG_OFFSET(VPCM_GAMCOR_RAMA_OFFSET_B);
192 gam_regs.offset_g = REG_OFFSET(VPCM_GAMCOR_RAMA_OFFSET_G);
193 gam_regs.offset_r = REG_OFFSET(VPCM_GAMCOR_RAMA_OFFSET_R);
194 gam_regs.start_base_cntl_b = REG_OFFSET(VPCM_GAMCOR_RAMA_START_BASE_CNTL_B);
195 gam_regs.start_base_cntl_g = REG_OFFSET(VPCM_GAMCOR_RAMA_START_BASE_CNTL_G);
196 gam_regs.start_base_cntl_r = REG_OFFSET(VPCM_GAMCOR_RAMA_START_BASE_CNTL_R);
197
198 vpe10_dpp_gamcor_reg_field(dpp, &gam_regs);
199
200 vpe10_cm_helper_program_gamcor_xfer_func(config_writer, params, &gam_regs);
201 vpe10_dpp_program_gammcor_lut(dpp, params->rgb_resulted, params->hw_points_num);
202 }
203
vpe10_dpp_program_input_transfer_func(struct dpp * dpp,struct transfer_func * input_tf)204 void vpe10_dpp_program_input_transfer_func(struct dpp *dpp, struct transfer_func *input_tf)
205 {
206 struct pwl_params *params = NULL;
207
208 PROGRAM_ENTRY();
209
210 struct stream_ctx *stream_ctx = &vpe_priv->stream_ctx[vpe_priv->fe_cb_ctx.stream_idx];
211 bool bypass;
212
213 VPE_ASSERT(input_tf);
214 // There should always have input_tf
215 // Only accept either DISTRIBUTED_POINTS or BYPASS
216 // No support for PREDEFINED case
217 VPE_ASSERT(input_tf->type == TF_TYPE_DISTRIBUTED_POINTS || input_tf->type == TF_TYPE_BYPASS);
218
219 // VPE always do NL scaling using gamcor, thus skipping dgam (default bypass)
220 // dpp->funcs->program_pre_dgam(dpp, tf);
221 if (input_tf->type == TF_TYPE_DISTRIBUTED_POINTS) {
222 vpe10_cm_helper_translate_curve_to_degamma_hw_format(
223 input_tf, &dpp->degamma_params, input_tf->dirty[dpp->inst]);
224 params = &dpp->degamma_params;
225 }
226
227 bypass = ((input_tf->type == TF_TYPE_BYPASS) || dpp->vpe_priv->init.debug.bypass_gamcor);
228
229 CONFIG_CACHE(input_tf, stream_ctx, vpe_priv->init.debug.disable_lut_caching, bypass,
230 vpe10_dpp_program_gamcor_lut(dpp, params), dpp->inst);
231 }
232
vpe10_dpp_program_gamut_remap(struct dpp * dpp,struct colorspace_transform * gamut_remap)233 void vpe10_dpp_program_gamut_remap(struct dpp *dpp, struct colorspace_transform *gamut_remap)
234 {
235 struct color_matrices_reg gam_regs;
236 uint16_t arr_reg_val[12];
237
238 PROGRAM_ENTRY();
239
240 if (!gamut_remap || !gamut_remap->enable_remap ||
241 dpp->vpe_priv->init.debug.bypass_dpp_gamut_remap) {
242 REG_SET(VPCM_GAMUT_REMAP_CONTROL, 0, VPCM_GAMUT_REMAP_MODE, 0);
243 return;
244 }
245
246 gam_regs.shifts.csc_c11 = REG_FIELD_SHIFT(VPCM_GAMUT_REMAP_C11);
247 gam_regs.masks.csc_c11 = REG_FIELD_MASK(VPCM_GAMUT_REMAP_C11);
248 gam_regs.shifts.csc_c12 = REG_FIELD_SHIFT(VPCM_GAMUT_REMAP_C12);
249 gam_regs.masks.csc_c12 = REG_FIELD_MASK(VPCM_GAMUT_REMAP_C12);
250 gam_regs.csc_c11_c12 = REG_OFFSET(VPCM_GAMUT_REMAP_C11_C12);
251 gam_regs.csc_c33_c34 = REG_OFFSET(VPCM_GAMUT_REMAP_C33_C34);
252
253 conv_convert_float_matrix(arr_reg_val, gamut_remap->matrix, 12);
254
255 vpe10_cm_helper_program_color_matrices(config_writer, arr_reg_val, &gam_regs);
256
257 REG_SET(VPCM_GAMUT_REMAP_CONTROL, 0, VPCM_GAMUT_REMAP_MODE, 1);
258 }
259
260 /*program post scaler scs block in dpp CM*/
vpe10_dpp_program_post_csc(struct dpp * dpp,enum color_space color_space,enum input_csc_select input_select,struct vpe_csc_matrix * input_cs)261 void vpe10_dpp_program_post_csc(struct dpp *dpp, enum color_space color_space,
262 enum input_csc_select input_select, struct vpe_csc_matrix *input_cs)
263 {
264 PROGRAM_ENTRY();
265 int i;
266 int arr_size = sizeof(vpe_input_csc_matrix_fixed) / sizeof(struct vpe_csc_matrix);
267 const uint16_t *regval = NULL;
268 struct color_matrices_reg gam_regs;
269
270 if (input_select == INPUT_CSC_SELECT_BYPASS || dpp->vpe_priv->init.debug.bypass_post_csc) {
271 REG_SET(VPCM_POST_CSC_CONTROL, 0, VPCM_POST_CSC_MODE, 0);
272 return;
273 }
274
275 if (input_cs == NULL) {
276 for (i = 0; i < arr_size; i++)
277 if (vpe_input_csc_matrix_fixed[i].cs == color_space) {
278 regval = vpe_input_csc_matrix_fixed[i].regval;
279 break;
280 }
281
282 if (regval == NULL) {
283 VPE_ASSERT(0);
284 return;
285 }
286 } else {
287 regval = input_cs->regval;
288 }
289
290 /* Always use the only one set of CSC matrix
291 */
292
293 gam_regs.shifts.csc_c11 = REG_FIELD_SHIFT(VPCM_POST_CSC_C11);
294 gam_regs.masks.csc_c11 = REG_FIELD_MASK(VPCM_POST_CSC_C11);
295 gam_regs.shifts.csc_c12 = REG_FIELD_SHIFT(VPCM_POST_CSC_C12);
296 gam_regs.masks.csc_c12 = REG_FIELD_MASK(VPCM_POST_CSC_C12);
297 gam_regs.csc_c11_c12 = REG_OFFSET(VPCM_POST_CSC_C11_C12);
298 gam_regs.csc_c33_c34 = REG_OFFSET(VPCM_POST_CSC_C33_C34);
299
300 vpe10_cm_helper_program_color_matrices(config_writer, regval, &gam_regs);
301
302 REG_SET(VPCM_POST_CSC_CONTROL, 0, VPCM_POST_CSC_MODE, input_select);
303 }
304
vpe10_dpp_set_hdr_multiplier(struct dpp * dpp,uint32_t multiplier)305 void vpe10_dpp_set_hdr_multiplier(struct dpp *dpp, uint32_t multiplier)
306 {
307 PROGRAM_ENTRY();
308
309 REG_SET(VPCM_HDR_MULT_COEF, REG_DEFAULT(VPCM_HDR_MULT_COEF), VPCM_HDR_MULT_COEF, multiplier);
310 }
311