1 | /* |
2 | * Copyright 2012-15 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 "dcn10_opp.h" |
28 | #include "reg_helper.h" |
29 | |
30 | #define REG(reg) \ |
31 | (oppn10->regs->reg) |
32 | |
33 | #undef FN |
34 | #define FN(reg_name, field_name) \ |
35 | oppn10->opp_shift->field_name, oppn10->opp_mask->field_name |
36 | |
37 | #define CTX \ |
38 | oppn10->base.ctx |
39 | |
40 | /** |
41 | * opp1_set_truncation(): |
42 | * 1) set truncation depth: 0 for 18 bpp or 1 for 24 bpp |
43 | * 2) enable truncation |
44 | * 3) HW remove 12bit FMT support for DCE11 power saving reason. |
45 | * |
46 | * @oppn10: output_pixel_processor struct instance for dcn10. |
47 | * @params: pointer to bit_depth_reduction_params. |
48 | */ |
49 | static void opp1_set_truncation( |
50 | struct dcn10_opp *oppn10, |
51 | const struct bit_depth_reduction_params *params) |
52 | { |
53 | REG_UPDATE_3(FMT_BIT_DEPTH_CONTROL, |
54 | FMT_TRUNCATE_EN, params->flags.TRUNCATE_ENABLED, |
55 | FMT_TRUNCATE_DEPTH, params->flags.TRUNCATE_DEPTH, |
56 | FMT_TRUNCATE_MODE, params->flags.TRUNCATE_MODE); |
57 | } |
58 | |
59 | static void opp1_set_spatial_dither( |
60 | struct dcn10_opp *oppn10, |
61 | const struct bit_depth_reduction_params *params) |
62 | { |
63 | /*Disable spatial (random) dithering*/ |
64 | REG_UPDATE_7(FMT_BIT_DEPTH_CONTROL, |
65 | FMT_SPATIAL_DITHER_EN, 0, |
66 | FMT_SPATIAL_DITHER_MODE, 0, |
67 | FMT_SPATIAL_DITHER_DEPTH, 0, |
68 | FMT_TEMPORAL_DITHER_EN, 0, |
69 | FMT_HIGHPASS_RANDOM_ENABLE, 0, |
70 | FMT_FRAME_RANDOM_ENABLE, 0, |
71 | FMT_RGB_RANDOM_ENABLE, 0); |
72 | |
73 | |
74 | /* only use FRAME_COUNTER_MAX if frameRandom == 1*/ |
75 | if (params->flags.FRAME_RANDOM == 1) { |
76 | if (params->flags.SPATIAL_DITHER_DEPTH == 0 || params->flags.SPATIAL_DITHER_DEPTH == 1) { |
77 | REG_UPDATE_2(FMT_CONTROL, |
78 | FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX, 15, |
79 | FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP, 2); |
80 | } else if (params->flags.SPATIAL_DITHER_DEPTH == 2) { |
81 | REG_UPDATE_2(FMT_CONTROL, |
82 | FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX, 3, |
83 | FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP, 1); |
84 | } else { |
85 | return; |
86 | } |
87 | } else { |
88 | REG_UPDATE_2(FMT_CONTROL, |
89 | FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX, 0, |
90 | FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP, 0); |
91 | } |
92 | |
93 | /*Set seed for random values for |
94 | * spatial dithering for R,G,B channels*/ |
95 | |
96 | REG_SET(FMT_DITHER_RAND_R_SEED, 0, |
97 | FMT_RAND_R_SEED, params->r_seed_value); |
98 | |
99 | REG_SET(FMT_DITHER_RAND_G_SEED, 0, |
100 | FMT_RAND_G_SEED, params->g_seed_value); |
101 | |
102 | REG_SET(FMT_DITHER_RAND_B_SEED, 0, |
103 | FMT_RAND_B_SEED, params->b_seed_value); |
104 | |
105 | /* FMT_OFFSET_R_Cr 31:16 0x0 Setting the zero |
106 | * offset for the R/Cr channel, lower 4LSB |
107 | * is forced to zeros. Typically set to 0 |
108 | * RGB and 0x80000 YCbCr. |
109 | */ |
110 | /* FMT_OFFSET_G_Y 31:16 0x0 Setting the zero |
111 | * offset for the G/Y channel, lower 4LSB is |
112 | * forced to zeros. Typically set to 0 RGB |
113 | * and 0x80000 YCbCr. |
114 | */ |
115 | /* FMT_OFFSET_B_Cb 31:16 0x0 Setting the zero |
116 | * offset for the B/Cb channel, lower 4LSB is |
117 | * forced to zeros. Typically set to 0 RGB and |
118 | * 0x80000 YCbCr. |
119 | */ |
120 | |
121 | REG_UPDATE_6(FMT_BIT_DEPTH_CONTROL, |
122 | /*Enable spatial dithering*/ |
123 | FMT_SPATIAL_DITHER_EN, params->flags.SPATIAL_DITHER_ENABLED, |
124 | /* Set spatial dithering mode |
125 | * (default is Seed patterrn AAAA...) |
126 | */ |
127 | FMT_SPATIAL_DITHER_MODE, params->flags.SPATIAL_DITHER_MODE, |
128 | /*Set spatial dithering bit depth*/ |
129 | FMT_SPATIAL_DITHER_DEPTH, params->flags.SPATIAL_DITHER_DEPTH, |
130 | /*Disable High pass filter*/ |
131 | FMT_HIGHPASS_RANDOM_ENABLE, params->flags.HIGHPASS_RANDOM, |
132 | /*Reset only at startup*/ |
133 | FMT_FRAME_RANDOM_ENABLE, params->flags.FRAME_RANDOM, |
134 | /*Set RGB data dithered with x^28+x^3+1*/ |
135 | FMT_RGB_RANDOM_ENABLE, params->flags.RGB_RANDOM); |
136 | } |
137 | |
138 | void opp1_program_bit_depth_reduction( |
139 | struct output_pixel_processor *opp, |
140 | const struct bit_depth_reduction_params *params) |
141 | { |
142 | struct dcn10_opp *oppn10 = TO_DCN10_OPP(opp); |
143 | |
144 | opp1_set_truncation(oppn10, params); |
145 | opp1_set_spatial_dither(oppn10, params); |
146 | /* TODO |
147 | * set_temporal_dither(oppn10, params); |
148 | */ |
149 | } |
150 | |
151 | /** |
152 | * opp1_set_pixel_encoding(): |
153 | * 0: RGB 4:4:4 or YCbCr 4:4:4 or YOnly |
154 | * 1: YCbCr 4:2:2 |
155 | * |
156 | * @oppn10: output_pixel_processor struct instance for dcn10. |
157 | * @params: pointer to clamping_and_pixel_encoding_params. |
158 | */ |
159 | static void opp1_set_pixel_encoding( |
160 | struct dcn10_opp *oppn10, |
161 | const struct clamping_and_pixel_encoding_params *params) |
162 | { |
163 | switch (params->pixel_encoding) { |
164 | |
165 | case PIXEL_ENCODING_RGB: |
166 | case PIXEL_ENCODING_YCBCR444: |
167 | REG_UPDATE(FMT_CONTROL, FMT_PIXEL_ENCODING, 0); |
168 | break; |
169 | case PIXEL_ENCODING_YCBCR422: |
170 | REG_UPDATE_3(FMT_CONTROL, |
171 | FMT_PIXEL_ENCODING, 1, |
172 | FMT_SUBSAMPLING_MODE, 2, |
173 | FMT_CBCR_BIT_REDUCTION_BYPASS, 0); |
174 | break; |
175 | case PIXEL_ENCODING_YCBCR420: |
176 | REG_UPDATE(FMT_CONTROL, FMT_PIXEL_ENCODING, 2); |
177 | break; |
178 | default: |
179 | break; |
180 | } |
181 | } |
182 | |
183 | /** |
184 | * opp1_set_clamping(): |
185 | * 1) Set clamping format based on bpc - 0 for 6bpc (No clamping) |
186 | * 1 for 8 bpc |
187 | * 2 for 10 bpc |
188 | * 3 for 12 bpc |
189 | * 7 for programable |
190 | * 2) Enable clamp if Limited range requested |
191 | * |
192 | * @oppn10: output_pixel_processor struct instance for dcn10. |
193 | * @params: pointer to clamping_and_pixel_encoding_params. |
194 | */ |
195 | static void opp1_set_clamping( |
196 | struct dcn10_opp *oppn10, |
197 | const struct clamping_and_pixel_encoding_params *params) |
198 | { |
199 | REG_UPDATE_2(FMT_CLAMP_CNTL, |
200 | FMT_CLAMP_DATA_EN, 0, |
201 | FMT_CLAMP_COLOR_FORMAT, 0); |
202 | |
203 | switch (params->clamping_level) { |
204 | case CLAMPING_FULL_RANGE: |
205 | REG_UPDATE_2(FMT_CLAMP_CNTL, |
206 | FMT_CLAMP_DATA_EN, 1, |
207 | FMT_CLAMP_COLOR_FORMAT, 0); |
208 | break; |
209 | case CLAMPING_LIMITED_RANGE_8BPC: |
210 | REG_UPDATE_2(FMT_CLAMP_CNTL, |
211 | FMT_CLAMP_DATA_EN, 1, |
212 | FMT_CLAMP_COLOR_FORMAT, 1); |
213 | break; |
214 | case CLAMPING_LIMITED_RANGE_10BPC: |
215 | REG_UPDATE_2(FMT_CLAMP_CNTL, |
216 | FMT_CLAMP_DATA_EN, 1, |
217 | FMT_CLAMP_COLOR_FORMAT, 2); |
218 | |
219 | break; |
220 | case CLAMPING_LIMITED_RANGE_12BPC: |
221 | REG_UPDATE_2(FMT_CLAMP_CNTL, |
222 | FMT_CLAMP_DATA_EN, 1, |
223 | FMT_CLAMP_COLOR_FORMAT, 3); |
224 | break; |
225 | case CLAMPING_LIMITED_RANGE_PROGRAMMABLE: |
226 | /* TODO */ |
227 | default: |
228 | break; |
229 | } |
230 | |
231 | } |
232 | |
233 | void opp1_set_dyn_expansion( |
234 | struct output_pixel_processor *opp, |
235 | enum dc_color_space color_sp, |
236 | enum dc_color_depth color_dpth, |
237 | enum signal_type signal) |
238 | { |
239 | struct dcn10_opp *oppn10 = TO_DCN10_OPP(opp); |
240 | |
241 | REG_UPDATE_2(FMT_DYNAMIC_EXP_CNTL, |
242 | FMT_DYNAMIC_EXP_EN, 0, |
243 | FMT_DYNAMIC_EXP_MODE, 0); |
244 | |
245 | if (opp->dyn_expansion == DYN_EXPANSION_DISABLE) |
246 | return; |
247 | |
248 | /*00 - 10-bit -> 12-bit dynamic expansion*/ |
249 | /*01 - 8-bit -> 12-bit dynamic expansion*/ |
250 | if (signal == SIGNAL_TYPE_HDMI_TYPE_A || |
251 | signal == SIGNAL_TYPE_DISPLAY_PORT || |
252 | signal == SIGNAL_TYPE_DISPLAY_PORT_MST || |
253 | signal == SIGNAL_TYPE_VIRTUAL) { |
254 | switch (color_dpth) { |
255 | case COLOR_DEPTH_888: |
256 | REG_UPDATE_2(FMT_DYNAMIC_EXP_CNTL, |
257 | FMT_DYNAMIC_EXP_EN, 1, |
258 | FMT_DYNAMIC_EXP_MODE, 1); |
259 | break; |
260 | case COLOR_DEPTH_101010: |
261 | REG_UPDATE_2(FMT_DYNAMIC_EXP_CNTL, |
262 | FMT_DYNAMIC_EXP_EN, 1, |
263 | FMT_DYNAMIC_EXP_MODE, 0); |
264 | break; |
265 | case COLOR_DEPTH_121212: |
266 | REG_UPDATE_2(FMT_DYNAMIC_EXP_CNTL, |
267 | FMT_DYNAMIC_EXP_EN, 1,/*otherwise last two bits are zero*/ |
268 | FMT_DYNAMIC_EXP_MODE, 0); |
269 | break; |
270 | default: |
271 | break; |
272 | } |
273 | } |
274 | } |
275 | |
276 | static void opp1_program_clamping_and_pixel_encoding( |
277 | struct output_pixel_processor *opp, |
278 | const struct clamping_and_pixel_encoding_params *params) |
279 | { |
280 | struct dcn10_opp *oppn10 = TO_DCN10_OPP(opp); |
281 | |
282 | opp1_set_clamping(oppn10, params); |
283 | opp1_set_pixel_encoding(oppn10, params); |
284 | } |
285 | |
286 | void opp1_program_fmt( |
287 | struct output_pixel_processor *opp, |
288 | struct bit_depth_reduction_params *fmt_bit_depth, |
289 | struct clamping_and_pixel_encoding_params *clamping) |
290 | { |
291 | struct dcn10_opp *oppn10 = TO_DCN10_OPP(opp); |
292 | |
293 | if (clamping->pixel_encoding == PIXEL_ENCODING_YCBCR420) |
294 | REG_UPDATE(FMT_MAP420_MEMORY_CONTROL, FMT_MAP420MEM_PWR_FORCE, 0); |
295 | |
296 | /* dithering is affected by <CrtcSourceSelect>, hence should be |
297 | * programmed afterwards */ |
298 | opp1_program_bit_depth_reduction( |
299 | opp, |
300 | params: fmt_bit_depth); |
301 | |
302 | opp1_program_clamping_and_pixel_encoding( |
303 | opp, |
304 | params: clamping); |
305 | |
306 | return; |
307 | } |
308 | |
309 | void opp1_program_stereo( |
310 | struct output_pixel_processor *opp, |
311 | bool enable, |
312 | const struct dc_crtc_timing *timing) |
313 | { |
314 | struct dcn10_opp *oppn10 = TO_DCN10_OPP(opp); |
315 | |
316 | uint32_t active_width = timing->h_addressable - timing->h_border_right - timing->h_border_right; |
317 | uint32_t space1_size = timing->v_total - timing->v_addressable; |
318 | /* TODO: confirm computation of space2_size */ |
319 | uint32_t space2_size = timing->v_total - timing->v_addressable; |
320 | |
321 | if (!enable) { |
322 | active_width = 0; |
323 | space1_size = 0; |
324 | space2_size = 0; |
325 | } |
326 | |
327 | /* TODO: for which cases should FMT_STEREOSYNC_OVERRIDE be set? */ |
328 | REG_UPDATE(FMT_CONTROL, FMT_STEREOSYNC_OVERRIDE, 0); |
329 | |
330 | REG_UPDATE(OPPBUF_CONTROL, OPPBUF_ACTIVE_WIDTH, active_width); |
331 | |
332 | /* Program OPPBUF_3D_VACT_SPACE1_SIZE and OPPBUF_VACT_SPACE2_SIZE registers |
333 | * In 3D progressive frames, Vactive space happens only in between the 2 frames, |
334 | * so only need to program OPPBUF_3D_VACT_SPACE1_SIZE |
335 | * In 3D alternative frames, left and right frames, top and bottom field. |
336 | */ |
337 | if (timing->timing_3d_format == TIMING_3D_FORMAT_FRAME_ALTERNATE) |
338 | REG_UPDATE(OPPBUF_3D_PARAMETERS_0, OPPBUF_3D_VACT_SPACE2_SIZE, space2_size); |
339 | else |
340 | REG_UPDATE(OPPBUF_3D_PARAMETERS_0, OPPBUF_3D_VACT_SPACE1_SIZE, space1_size); |
341 | |
342 | /* TODO: Is programming of OPPBUF_DUMMY_DATA_R/G/B needed? */ |
343 | /* |
344 | REG_UPDATE(OPPBUF_3D_PARAMETERS_0, |
345 | OPPBUF_DUMMY_DATA_R, data_r); |
346 | REG_UPDATE(OPPBUF_3D_PARAMETERS_1, |
347 | OPPBUF_DUMMY_DATA_G, data_g); |
348 | REG_UPDATE(OPPBUF_3D_PARAMETERS_1, |
349 | OPPBUF_DUMMY_DATA_B, _data_b); |
350 | */ |
351 | } |
352 | |
353 | void opp1_pipe_clock_control(struct output_pixel_processor *opp, bool enable) |
354 | { |
355 | struct dcn10_opp *oppn10 = TO_DCN10_OPP(opp); |
356 | uint32_t regval = enable ? 1 : 0; |
357 | |
358 | REG_UPDATE(OPP_PIPE_CONTROL, OPP_PIPE_CLOCK_EN, regval); |
359 | } |
360 | |
361 | /*****************************************/ |
362 | /* Constructor, Destructor */ |
363 | /*****************************************/ |
364 | |
365 | void opp1_destroy(struct output_pixel_processor **opp) |
366 | { |
367 | kfree(TO_DCN10_OPP(*opp)); |
368 | *opp = NULL; |
369 | } |
370 | |
371 | static const struct opp_funcs dcn10_opp_funcs = { |
372 | .opp_set_dyn_expansion = opp1_set_dyn_expansion, |
373 | .opp_program_fmt = opp1_program_fmt, |
374 | .opp_program_bit_depth_reduction = opp1_program_bit_depth_reduction, |
375 | .opp_program_stereo = opp1_program_stereo, |
376 | .opp_pipe_clock_control = opp1_pipe_clock_control, |
377 | .opp_set_disp_pattern_generator = NULL, |
378 | .opp_program_dpg_dimensions = NULL, |
379 | .dpg_is_blanked = NULL, |
380 | .opp_destroy = opp1_destroy |
381 | }; |
382 | |
383 | void dcn10_opp_construct(struct dcn10_opp *oppn10, |
384 | struct dc_context *ctx, |
385 | uint32_t inst, |
386 | const struct dcn10_opp_registers *regs, |
387 | const struct dcn10_opp_shift *opp_shift, |
388 | const struct dcn10_opp_mask *opp_mask) |
389 | { |
390 | |
391 | oppn10->base.ctx = ctx; |
392 | oppn10->base.inst = inst; |
393 | oppn10->base.funcs = &dcn10_opp_funcs; |
394 | |
395 | oppn10->regs = regs; |
396 | oppn10->opp_shift = opp_shift; |
397 | oppn10->opp_mask = opp_mask; |
398 | } |
399 | |