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