1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Support for Intel Camera Imaging ISP subsystem.
4 * Copyright (c) 2010 - 2015, Intel Corporation.
5 */
6
7 #include <linux/math.h>
8
9 #include "system_global.h"
10
11 #include "ia_css_ifmtr.h"
12 #include <math_support.h>
13 #include "sh_css_internal.h"
14 #include "input_formatter.h"
15 #include "assert_support.h"
16 #include "sh_css_sp.h"
17 #include "isp/modes/interface/input_buf.isp.h"
18
19 /************************************************************
20 * Static functions declarations
21 ************************************************************/
22 static int ifmtr_start_column(
23 const struct ia_css_stream_config *config,
24 unsigned int bin_in,
25 unsigned int *start_column);
26
27 static int ifmtr_input_start_line(
28 const struct ia_css_stream_config *config,
29 unsigned int bin_in,
30 unsigned int *start_line);
31
32 static void ifmtr_set_if_blocking_mode(
33 const input_formatter_cfg_t *const config_a,
34 const input_formatter_cfg_t *const config_b);
35
36 /************************************************************
37 * Public functions
38 ************************************************************/
39
40 /* ISP expects GRBG bayer order, we skip one line and/or one row
41 * to correct in case the input bayer order is different.
42 */
ia_css_ifmtr_lines_needed_for_bayer_order(const struct ia_css_stream_config * config)43 unsigned int ia_css_ifmtr_lines_needed_for_bayer_order(
44 const struct ia_css_stream_config *config)
45 {
46 assert(config);
47 if ((config->input_config.bayer_order == IA_CSS_BAYER_ORDER_BGGR)
48 || (config->input_config.bayer_order == IA_CSS_BAYER_ORDER_GBRG))
49 return 1;
50
51 return 0;
52 }
53
ia_css_ifmtr_columns_needed_for_bayer_order(const struct ia_css_stream_config * config)54 unsigned int ia_css_ifmtr_columns_needed_for_bayer_order(
55 const struct ia_css_stream_config *config)
56 {
57 assert(config);
58 if ((config->input_config.bayer_order == IA_CSS_BAYER_ORDER_RGGB)
59 || (config->input_config.bayer_order == IA_CSS_BAYER_ORDER_GBRG))
60 return 1;
61
62 return 0;
63 }
64
ia_css_ifmtr_configure(struct ia_css_stream_config * config,struct ia_css_binary * binary)65 int ia_css_ifmtr_configure(struct ia_css_stream_config *config,
66 struct ia_css_binary *binary)
67 {
68 unsigned int start_line, start_column = 0,
69 cropped_height,
70 cropped_width,
71 num_vectors,
72 buffer_height = 2,
73 buffer_width,
74 two_ppc,
75 vmem_increment = 0,
76 deinterleaving = 0,
77 deinterleaving_b = 0,
78 width_a = 0,
79 width_b = 0,
80 bits_per_pixel,
81 vectors_per_buffer,
82 vectors_per_line = 0,
83 buffers_per_line = 0,
84 buf_offset_a = 0,
85 buf_offset_b = 0,
86 line_width = 0,
87 width_b_factor = 1, start_column_b,
88 left_padding = 0;
89 input_formatter_cfg_t if_a_config, if_b_config;
90 enum atomisp_input_format input_format;
91 int err = 0;
92 u8 if_config_index;
93
94 /* Determine which input formatter config set is targeted. */
95 /* Index is equal to the CSI-2 port used. */
96 enum mipi_port_id port;
97
98 if (binary) {
99 cropped_height = binary->in_frame_info.res.height;
100 cropped_width = binary->in_frame_info.res.width;
101 /* This should correspond to the input buffer definition for
102 ISP binaries in input_buf.isp.h */
103 if (binary->info->sp.enable.continuous &&
104 binary->info->sp.pipeline.mode != IA_CSS_BINARY_MODE_COPY)
105 buffer_width = MAX_VECTORS_PER_INPUT_LINE_CONT * ISP_VEC_NELEMS;
106 else
107 buffer_width = binary->info->sp.input.max_width;
108 input_format = binary->input_format;
109 } else {
110 /* sp raw copy pipe (IA_CSS_PIPE_MODE_COPY): binary is NULL */
111 cropped_height = config->input_config.input_res.height;
112 cropped_width = config->input_config.input_res.width;
113 buffer_width = MAX_VECTORS_PER_INPUT_LINE_CONT * ISP_VEC_NELEMS;
114 input_format = config->input_config.format;
115 }
116 two_ppc = config->pixels_per_clock == 2;
117 if (config->mode == IA_CSS_INPUT_MODE_SENSOR
118 || config->mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR) {
119 port = config->source.port.port;
120 if_config_index = (uint8_t)(port - MIPI_PORT0_ID);
121 } else if (config->mode == IA_CSS_INPUT_MODE_MEMORY) {
122 if_config_index = SH_CSS_IF_CONFIG_NOT_NEEDED;
123 } else {
124 if_config_index = 0;
125 }
126
127 assert(if_config_index <= SH_CSS_MAX_IF_CONFIGS
128 || if_config_index == SH_CSS_IF_CONFIG_NOT_NEEDED);
129
130 /* TODO: check to see if input is RAW and if current mode interprets
131 * RAW data in any particular bayer order. copy binary with output
132 * format other than raw should not result in dropping lines and/or
133 * columns.
134 */
135 err = ifmtr_input_start_line(config, cropped_height, &start_line);
136 if (err)
137 return err;
138 err = ifmtr_start_column(config, cropped_width, &start_column);
139 if (err)
140 return err;
141
142 if (config->left_padding == -1)
143 if (!binary)
144 /* sp raw copy pipe: set left_padding value */
145 left_padding = 0;
146 else
147 left_padding = binary->left_padding;
148 else
149 left_padding = 2 * ISP_VEC_NELEMS - config->left_padding;
150
151 if (left_padding) {
152 num_vectors = DIV_ROUND_UP(cropped_width + left_padding, ISP_VEC_NELEMS);
153 } else {
154 num_vectors = DIV_ROUND_UP(cropped_width, ISP_VEC_NELEMS);
155 num_vectors *= buffer_height;
156 /* todo: in case of left padding,
157 num_vectors is vectors per line,
158 otherwise vectors per line * buffer_height. */
159 }
160
161 start_column_b = start_column;
162
163 bits_per_pixel = input_formatter_get_alignment(INPUT_FORMATTER0_ID)
164 * 8 / ISP_VEC_NELEMS;
165 switch (input_format) {
166 case ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY:
167 if (two_ppc) {
168 vmem_increment = 1;
169 deinterleaving = 1;
170 deinterleaving_b = 1;
171 /* half lines */
172 width_a = cropped_width * deinterleaving / 2;
173 width_b_factor = 2;
174 /* full lines */
175 width_b = width_a * width_b_factor;
176 buffer_width *= deinterleaving * 2;
177 /* Patch from bayer to yuv */
178 num_vectors *= deinterleaving;
179 buf_offset_b = buffer_width / 2 / ISP_VEC_NELEMS;
180 vectors_per_line = num_vectors / buffer_height;
181 /* Even lines are half size */
182 line_width = vectors_per_line *
183 input_formatter_get_alignment(INPUT_FORMATTER0_ID) /
184 2;
185 start_column /= 2;
186 } else {
187 vmem_increment = 1;
188 deinterleaving = 3;
189 width_a = cropped_width * deinterleaving / 2;
190 buffer_width = buffer_width * deinterleaving / 2;
191 /* Patch from bayer to yuv */
192 num_vectors = num_vectors / 2 * deinterleaving;
193 start_column = start_column * deinterleaving / 2;
194 }
195 break;
196 case ATOMISP_INPUT_FORMAT_YUV420_8:
197 case ATOMISP_INPUT_FORMAT_YUV420_10:
198 case ATOMISP_INPUT_FORMAT_YUV420_16:
199 if (two_ppc) {
200 vmem_increment = 1;
201 deinterleaving = 1;
202 width_a = width_b = cropped_width * deinterleaving / 2;
203 buffer_width *= deinterleaving * 2;
204 num_vectors *= deinterleaving;
205 buf_offset_b = buffer_width / 2 / ISP_VEC_NELEMS;
206 vectors_per_line = num_vectors / buffer_height;
207 /* Even lines are half size */
208 line_width = vectors_per_line *
209 input_formatter_get_alignment(INPUT_FORMATTER0_ID) /
210 2;
211 start_column *= deinterleaving;
212 start_column /= 2;
213 start_column_b = start_column;
214 } else {
215 vmem_increment = 1;
216 deinterleaving = 1;
217 width_a = cropped_width * deinterleaving;
218 buffer_width *= deinterleaving * 2;
219 num_vectors *= deinterleaving;
220 start_column *= deinterleaving;
221 }
222 break;
223 case ATOMISP_INPUT_FORMAT_YUV422_8:
224 case ATOMISP_INPUT_FORMAT_YUV422_10:
225 case ATOMISP_INPUT_FORMAT_YUV422_16:
226 if (two_ppc) {
227 vmem_increment = 1;
228 deinterleaving = 1;
229 width_a = width_b = cropped_width * deinterleaving;
230 buffer_width *= deinterleaving * 2;
231 num_vectors *= deinterleaving;
232 start_column *= deinterleaving;
233 buf_offset_b = buffer_width / 2 / ISP_VEC_NELEMS;
234 start_column_b = start_column;
235 } else {
236 vmem_increment = 1;
237 deinterleaving = 2;
238 width_a = cropped_width * deinterleaving;
239 buffer_width *= deinterleaving;
240 num_vectors *= deinterleaving;
241 start_column *= deinterleaving;
242 }
243 break;
244 case ATOMISP_INPUT_FORMAT_RGB_444:
245 case ATOMISP_INPUT_FORMAT_RGB_555:
246 case ATOMISP_INPUT_FORMAT_RGB_565:
247 case ATOMISP_INPUT_FORMAT_RGB_666:
248 case ATOMISP_INPUT_FORMAT_RGB_888:
249 num_vectors *= 2;
250 if (two_ppc) {
251 deinterleaving = 2; /* BR in if_a, G in if_b */
252 deinterleaving_b = 1; /* BR in if_a, G in if_b */
253 buffers_per_line = 4;
254 start_column_b = start_column;
255 start_column *= deinterleaving;
256 start_column_b *= deinterleaving_b;
257 } else {
258 deinterleaving = 3; /* BGR */
259 buffers_per_line = 3;
260 start_column *= deinterleaving;
261 }
262 vmem_increment = 1;
263 width_a = cropped_width * deinterleaving;
264 width_b = cropped_width * deinterleaving_b;
265 buffer_width *= buffers_per_line;
266 /* Patch from bayer to rgb */
267 num_vectors = num_vectors / 2 * deinterleaving;
268 buf_offset_b = buffer_width / 2 / ISP_VEC_NELEMS;
269 break;
270 case ATOMISP_INPUT_FORMAT_RAW_6:
271 case ATOMISP_INPUT_FORMAT_RAW_7:
272 case ATOMISP_INPUT_FORMAT_RAW_8:
273 case ATOMISP_INPUT_FORMAT_RAW_10:
274 case ATOMISP_INPUT_FORMAT_RAW_12:
275 if (two_ppc) {
276 int crop_col = (start_column % 2) == 1;
277
278 vmem_increment = 2;
279 deinterleaving = 1;
280 width_a = width_b = cropped_width / 2;
281
282 /* When two_ppc is enabled AND we need to crop one extra
283 * column, if_a crops by one extra and we swap the
284 * output offsets to interleave the bayer pattern in
285 * the correct order.
286 */
287 buf_offset_a = crop_col ? 1 : 0;
288 buf_offset_b = crop_col ? 0 : 1;
289 start_column_b = start_column / 2;
290 start_column = start_column / 2 + crop_col;
291 } else {
292 vmem_increment = 1;
293 deinterleaving = 2;
294 if ((!binary) || (config->continuous && binary
295 && binary->info->sp.pipeline.mode == IA_CSS_BINARY_MODE_COPY)) {
296 /* !binary -> sp raw copy pipe, no deinterleaving */
297 deinterleaving = 1;
298 }
299 width_a = cropped_width;
300 /* Must be multiple of deinterleaving */
301 num_vectors = CEIL_MUL(num_vectors, deinterleaving);
302 }
303 buffer_height *= 2;
304 if ((!binary) || config->continuous)
305 /* !binary -> sp raw copy pipe */
306 buffer_height *= 2;
307 vectors_per_line = DIV_ROUND_UP(cropped_width, ISP_VEC_NELEMS);
308 vectors_per_line = CEIL_MUL(vectors_per_line, deinterleaving);
309 break;
310 case ATOMISP_INPUT_FORMAT_RAW_14:
311 case ATOMISP_INPUT_FORMAT_RAW_16:
312 if (two_ppc) {
313 num_vectors *= 2;
314 vmem_increment = 1;
315 deinterleaving = 2;
316 width_a = width_b = cropped_width;
317 /* B buffer is one line further */
318 buf_offset_b = buffer_width / ISP_VEC_NELEMS;
319 bits_per_pixel *= 2;
320 } else {
321 vmem_increment = 1;
322 deinterleaving = 2;
323 width_a = cropped_width;
324 start_column /= deinterleaving;
325 }
326 buffer_height *= 2;
327 break;
328 case ATOMISP_INPUT_FORMAT_BINARY_8:
329 case ATOMISP_INPUT_FORMAT_GENERIC_SHORT1:
330 case ATOMISP_INPUT_FORMAT_GENERIC_SHORT2:
331 case ATOMISP_INPUT_FORMAT_GENERIC_SHORT3:
332 case ATOMISP_INPUT_FORMAT_GENERIC_SHORT4:
333 case ATOMISP_INPUT_FORMAT_GENERIC_SHORT5:
334 case ATOMISP_INPUT_FORMAT_GENERIC_SHORT6:
335 case ATOMISP_INPUT_FORMAT_GENERIC_SHORT7:
336 case ATOMISP_INPUT_FORMAT_GENERIC_SHORT8:
337 case ATOMISP_INPUT_FORMAT_YUV420_8_SHIFT:
338 case ATOMISP_INPUT_FORMAT_YUV420_10_SHIFT:
339 case ATOMISP_INPUT_FORMAT_EMBEDDED:
340 case ATOMISP_INPUT_FORMAT_USER_DEF1:
341 case ATOMISP_INPUT_FORMAT_USER_DEF2:
342 case ATOMISP_INPUT_FORMAT_USER_DEF3:
343 case ATOMISP_INPUT_FORMAT_USER_DEF4:
344 case ATOMISP_INPUT_FORMAT_USER_DEF5:
345 case ATOMISP_INPUT_FORMAT_USER_DEF6:
346 case ATOMISP_INPUT_FORMAT_USER_DEF7:
347 case ATOMISP_INPUT_FORMAT_USER_DEF8:
348 break;
349 }
350 if (width_a == 0)
351 return -EINVAL;
352
353 if (two_ppc)
354 left_padding /= 2;
355
356 /* Default values */
357 if (left_padding)
358 vectors_per_line = num_vectors;
359 if (!vectors_per_line) {
360 vectors_per_line = CEIL_MUL(num_vectors / buffer_height,
361 deinterleaving);
362 line_width = 0;
363 }
364 if (!line_width)
365 line_width = vectors_per_line *
366 input_formatter_get_alignment(INPUT_FORMATTER0_ID);
367 if (!buffers_per_line)
368 buffers_per_line = deinterleaving;
369 line_width = CEIL_MUL(line_width,
370 input_formatter_get_alignment(INPUT_FORMATTER0_ID)
371 * vmem_increment);
372
373 vectors_per_buffer = buffer_height * buffer_width / ISP_VEC_NELEMS;
374
375 if_a_config.start_line = start_line;
376 if_a_config.start_column = start_column;
377 if_a_config.left_padding = left_padding / deinterleaving;
378 if_a_config.cropped_height = cropped_height;
379 if_a_config.cropped_width = width_a;
380 if_a_config.deinterleaving = deinterleaving;
381 if_a_config.buf_vecs = vectors_per_buffer;
382 if_a_config.buf_start_index = buf_offset_a;
383 if_a_config.buf_increment = vmem_increment;
384 if_a_config.buf_eol_offset =
385 buffer_width * bits_per_pixel / 8 - line_width;
386 if_a_config.is_yuv420_format =
387 (input_format == ATOMISP_INPUT_FORMAT_YUV420_8)
388 || (input_format == ATOMISP_INPUT_FORMAT_YUV420_10)
389 || (input_format == ATOMISP_INPUT_FORMAT_YUV420_16);
390 if_a_config.block_no_reqs = (config->mode != IA_CSS_INPUT_MODE_SENSOR);
391
392 if (two_ppc) {
393 if (deinterleaving_b) {
394 deinterleaving = deinterleaving_b;
395 width_b = cropped_width * deinterleaving;
396 buffer_width *= deinterleaving;
397 /* Patch from bayer to rgb */
398 num_vectors = num_vectors / 2 *
399 deinterleaving * width_b_factor;
400 vectors_per_line = num_vectors / buffer_height;
401 line_width = vectors_per_line *
402 input_formatter_get_alignment(INPUT_FORMATTER0_ID);
403 }
404 if_b_config.start_line = start_line;
405 if_b_config.start_column = start_column_b;
406 if_b_config.left_padding = left_padding / deinterleaving;
407 if_b_config.cropped_height = cropped_height;
408 if_b_config.cropped_width = width_b;
409 if_b_config.deinterleaving = deinterleaving;
410 if_b_config.buf_vecs = vectors_per_buffer;
411 if_b_config.buf_start_index = buf_offset_b;
412 if_b_config.buf_increment = vmem_increment;
413 if_b_config.buf_eol_offset =
414 buffer_width * bits_per_pixel / 8 - line_width;
415 if_b_config.is_yuv420_format =
416 input_format == ATOMISP_INPUT_FORMAT_YUV420_8
417 || input_format == ATOMISP_INPUT_FORMAT_YUV420_10
418 || input_format == ATOMISP_INPUT_FORMAT_YUV420_16;
419 if_b_config.block_no_reqs =
420 (config->mode != IA_CSS_INPUT_MODE_SENSOR);
421
422 if (if_config_index != SH_CSS_IF_CONFIG_NOT_NEEDED) {
423 assert(if_config_index <= SH_CSS_MAX_IF_CONFIGS);
424
425 ifmtr_set_if_blocking_mode(&if_a_config, &if_b_config);
426 /* Set the ifconfigs to SP group */
427 sh_css_sp_set_if_configs(&if_a_config, &if_b_config,
428 if_config_index);
429 }
430 } else {
431 if (if_config_index != SH_CSS_IF_CONFIG_NOT_NEEDED) {
432 assert(if_config_index <= SH_CSS_MAX_IF_CONFIGS);
433
434 ifmtr_set_if_blocking_mode(&if_a_config, NULL);
435 /* Set the ifconfigs to SP group */
436 sh_css_sp_set_if_configs(&if_a_config, NULL,
437 if_config_index);
438 }
439 }
440
441 return 0;
442 }
443
444 bool ifmtr_set_if_blocking_mode_reset = true;
445
446 /************************************************************
447 * Static functions
448 ************************************************************/
ifmtr_set_if_blocking_mode(const input_formatter_cfg_t * const config_a,const input_formatter_cfg_t * const config_b)449 static void ifmtr_set_if_blocking_mode(
450 const input_formatter_cfg_t *const config_a,
451 const input_formatter_cfg_t *const config_b)
452 {
453 int i;
454 bool block[] = { false, false, false, false };
455
456 assert(N_INPUT_FORMATTER_ID <= (ARRAY_SIZE(block)));
457
458 block[INPUT_FORMATTER0_ID] = (bool)config_a->block_no_reqs;
459 if (config_b)
460 block[INPUT_FORMATTER1_ID] = (bool)config_b->block_no_reqs;
461
462 /* TODO: next could cause issues when streams are started after
463 * eachother. */
464 /*IF should not be reconfigured/reset from host */
465 if (ifmtr_set_if_blocking_mode_reset) {
466 ifmtr_set_if_blocking_mode_reset = false;
467 for (i = 0; i < N_INPUT_FORMATTER_ID; i++) {
468 input_formatter_ID_t id = (input_formatter_ID_t)i;
469
470 input_formatter_rst(id);
471 input_formatter_set_fifo_blocking_mode(id, block[id]);
472 }
473 }
474
475 return;
476 }
477
ifmtr_start_column(const struct ia_css_stream_config * config,unsigned int bin_in,unsigned int * start_column)478 static int ifmtr_start_column(
479 const struct ia_css_stream_config *config,
480 unsigned int bin_in,
481 unsigned int *start_column)
482 {
483 unsigned int in = config->input_config.input_res.width, start,
484 for_bayer = ia_css_ifmtr_columns_needed_for_bayer_order(config);
485
486 if (bin_in + 2 * for_bayer > in)
487 return -EINVAL;
488
489 /* On the hardware, we want to use the middle of the input, so we
490 * divide the start column by 2. */
491 start = (in - bin_in) / 2;
492 /* in case the number of extra columns is 2 or odd, we round the start
493 * column down */
494 start &= ~0x1;
495
496 /* now we add the one column (if needed) to correct for the bayer
497 * order).
498 */
499 start += for_bayer;
500 *start_column = start;
501 return 0;
502 }
503
ifmtr_input_start_line(const struct ia_css_stream_config * config,unsigned int bin_in,unsigned int * start_line)504 static int ifmtr_input_start_line(
505 const struct ia_css_stream_config *config,
506 unsigned int bin_in,
507 unsigned int *start_line)
508 {
509 unsigned int in = config->input_config.input_res.height, start,
510 for_bayer = ia_css_ifmtr_lines_needed_for_bayer_order(config);
511
512 if (bin_in + 2 * for_bayer > in)
513 return -EINVAL;
514
515 /* On the hardware, we want to use the middle of the input, so we
516 * divide the start line by 2. On the simulator, we cannot handle extra
517 * lines at the end of the frame.
518 */
519 start = (in - bin_in) / 2;
520 /* in case the number of extra lines is 2 or odd, we round the start
521 * line down.
522 */
523 start &= ~0x1;
524
525 /* now we add the one line (if needed) to correct for the bayer order */
526 start += for_bayer;
527 *start_line = start;
528 return 0;
529 }
530
531