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