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 "system_global.h"
8 #include <linux/kernel.h>
9
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 = CEIL_DIV(cropped_width + left_padding,
153 ISP_VEC_NELEMS);
154 } else {
155 num_vectors = CEIL_DIV(cropped_width, ISP_VEC_NELEMS);
156 num_vectors *= buffer_height;
157 /* todo: in case of left padding,
158 num_vectors is vectors per line,
159 otherwise vectors per line * buffer_height. */
160 }
161
162 start_column_b = start_column;
163
164 bits_per_pixel = input_formatter_get_alignment(INPUT_FORMATTER0_ID)
165 * 8 / ISP_VEC_NELEMS;
166 switch (input_format) {
167 case ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY:
168 if (two_ppc) {
169 vmem_increment = 1;
170 deinterleaving = 1;
171 deinterleaving_b = 1;
172 /* half lines */
173 width_a = cropped_width * deinterleaving / 2;
174 width_b_factor = 2;
175 /* full lines */
176 width_b = width_a * width_b_factor;
177 buffer_width *= deinterleaving * 2;
178 /* Patch from bayer to yuv */
179 num_vectors *= deinterleaving;
180 buf_offset_b = buffer_width / 2 / ISP_VEC_NELEMS;
181 vectors_per_line = num_vectors / buffer_height;
182 /* Even lines are half size */
183 line_width = vectors_per_line *
184 input_formatter_get_alignment(INPUT_FORMATTER0_ID) /
185 2;
186 start_column /= 2;
187 } else {
188 vmem_increment = 1;
189 deinterleaving = 3;
190 width_a = cropped_width * deinterleaving / 2;
191 buffer_width = buffer_width * deinterleaving / 2;
192 /* Patch from bayer to yuv */
193 num_vectors = num_vectors / 2 * deinterleaving;
194 start_column = start_column * deinterleaving / 2;
195 }
196 break;
197 case ATOMISP_INPUT_FORMAT_YUV420_8:
198 case ATOMISP_INPUT_FORMAT_YUV420_10:
199 case ATOMISP_INPUT_FORMAT_YUV420_16:
200 if (two_ppc) {
201 vmem_increment = 1;
202 deinterleaving = 1;
203 width_a = width_b = cropped_width * deinterleaving / 2;
204 buffer_width *= deinterleaving * 2;
205 num_vectors *= deinterleaving;
206 buf_offset_b = buffer_width / 2 / ISP_VEC_NELEMS;
207 vectors_per_line = num_vectors / buffer_height;
208 /* Even lines are half size */
209 line_width = vectors_per_line *
210 input_formatter_get_alignment(INPUT_FORMATTER0_ID) /
211 2;
212 start_column *= deinterleaving;
213 start_column /= 2;
214 start_column_b = start_column;
215 } else {
216 vmem_increment = 1;
217 deinterleaving = 1;
218 width_a = cropped_width * deinterleaving;
219 buffer_width *= deinterleaving * 2;
220 num_vectors *= deinterleaving;
221 start_column *= deinterleaving;
222 }
223 break;
224 case ATOMISP_INPUT_FORMAT_YUV422_8:
225 case ATOMISP_INPUT_FORMAT_YUV422_10:
226 case ATOMISP_INPUT_FORMAT_YUV422_16:
227 if (two_ppc) {
228 vmem_increment = 1;
229 deinterleaving = 1;
230 width_a = width_b = cropped_width * deinterleaving;
231 buffer_width *= deinterleaving * 2;
232 num_vectors *= deinterleaving;
233 start_column *= deinterleaving;
234 buf_offset_b = buffer_width / 2 / ISP_VEC_NELEMS;
235 start_column_b = start_column;
236 } else {
237 vmem_increment = 1;
238 deinterleaving = 2;
239 width_a = cropped_width * deinterleaving;
240 buffer_width *= deinterleaving;
241 num_vectors *= deinterleaving;
242 start_column *= deinterleaving;
243 }
244 break;
245 case ATOMISP_INPUT_FORMAT_RGB_444:
246 case ATOMISP_INPUT_FORMAT_RGB_555:
247 case ATOMISP_INPUT_FORMAT_RGB_565:
248 case ATOMISP_INPUT_FORMAT_RGB_666:
249 case ATOMISP_INPUT_FORMAT_RGB_888:
250 num_vectors *= 2;
251 if (two_ppc) {
252 deinterleaving = 2; /* BR in if_a, G in if_b */
253 deinterleaving_b = 1; /* BR in if_a, G in if_b */
254 buffers_per_line = 4;
255 start_column_b = start_column;
256 start_column *= deinterleaving;
257 start_column_b *= deinterleaving_b;
258 } else {
259 deinterleaving = 3; /* BGR */
260 buffers_per_line = 3;
261 start_column *= deinterleaving;
262 }
263 vmem_increment = 1;
264 width_a = cropped_width * deinterleaving;
265 width_b = cropped_width * deinterleaving_b;
266 buffer_width *= buffers_per_line;
267 /* Patch from bayer to rgb */
268 num_vectors = num_vectors / 2 * deinterleaving;
269 buf_offset_b = buffer_width / 2 / ISP_VEC_NELEMS;
270 break;
271 case ATOMISP_INPUT_FORMAT_RAW_6:
272 case ATOMISP_INPUT_FORMAT_RAW_7:
273 case ATOMISP_INPUT_FORMAT_RAW_8:
274 case ATOMISP_INPUT_FORMAT_RAW_10:
275 case ATOMISP_INPUT_FORMAT_RAW_12:
276 if (two_ppc) {
277 int crop_col = (start_column % 2) == 1;
278
279 vmem_increment = 2;
280 deinterleaving = 1;
281 width_a = width_b = cropped_width / 2;
282
283 /* When two_ppc is enabled AND we need to crop one extra
284 * column, if_a crops by one extra and we swap the
285 * output offsets to interleave the bayer pattern in
286 * the correct order.
287 */
288 buf_offset_a = crop_col ? 1 : 0;
289 buf_offset_b = crop_col ? 0 : 1;
290 start_column_b = start_column / 2;
291 start_column = start_column / 2 + crop_col;
292 } else {
293 vmem_increment = 1;
294 deinterleaving = 2;
295 if ((!binary) || (config->continuous && binary
296 && binary->info->sp.pipeline.mode == IA_CSS_BINARY_MODE_COPY)) {
297 /* !binary -> sp raw copy pipe, no deinterleaving */
298 deinterleaving = 1;
299 }
300 width_a = cropped_width;
301 /* Must be multiple of deinterleaving */
302 num_vectors = CEIL_MUL(num_vectors, deinterleaving);
303 }
304 buffer_height *= 2;
305 if ((!binary) || config->continuous)
306 /* !binary -> sp raw copy pipe */
307 buffer_height *= 2;
308 vectors_per_line = CEIL_DIV(cropped_width, ISP_VEC_NELEMS);
309 vectors_per_line = CEIL_MUL(vectors_per_line, deinterleaving);
310 break;
311 case ATOMISP_INPUT_FORMAT_RAW_14:
312 case ATOMISP_INPUT_FORMAT_RAW_16:
313 if (two_ppc) {
314 num_vectors *= 2;
315 vmem_increment = 1;
316 deinterleaving = 2;
317 width_a = width_b = cropped_width;
318 /* B buffer is one line further */
319 buf_offset_b = buffer_width / ISP_VEC_NELEMS;
320 bits_per_pixel *= 2;
321 } else {
322 vmem_increment = 1;
323 deinterleaving = 2;
324 width_a = cropped_width;
325 start_column /= deinterleaving;
326 }
327 buffer_height *= 2;
328 break;
329 case ATOMISP_INPUT_FORMAT_BINARY_8:
330 case ATOMISP_INPUT_FORMAT_GENERIC_SHORT1:
331 case ATOMISP_INPUT_FORMAT_GENERIC_SHORT2:
332 case ATOMISP_INPUT_FORMAT_GENERIC_SHORT3:
333 case ATOMISP_INPUT_FORMAT_GENERIC_SHORT4:
334 case ATOMISP_INPUT_FORMAT_GENERIC_SHORT5:
335 case ATOMISP_INPUT_FORMAT_GENERIC_SHORT6:
336 case ATOMISP_INPUT_FORMAT_GENERIC_SHORT7:
337 case ATOMISP_INPUT_FORMAT_GENERIC_SHORT8:
338 case ATOMISP_INPUT_FORMAT_YUV420_8_SHIFT:
339 case ATOMISP_INPUT_FORMAT_YUV420_10_SHIFT:
340 case ATOMISP_INPUT_FORMAT_EMBEDDED:
341 case ATOMISP_INPUT_FORMAT_USER_DEF1:
342 case ATOMISP_INPUT_FORMAT_USER_DEF2:
343 case ATOMISP_INPUT_FORMAT_USER_DEF3:
344 case ATOMISP_INPUT_FORMAT_USER_DEF4:
345 case ATOMISP_INPUT_FORMAT_USER_DEF5:
346 case ATOMISP_INPUT_FORMAT_USER_DEF6:
347 case ATOMISP_INPUT_FORMAT_USER_DEF7:
348 case ATOMISP_INPUT_FORMAT_USER_DEF8:
349 break;
350 }
351 if (width_a == 0)
352 return -EINVAL;
353
354 if (two_ppc)
355 left_padding /= 2;
356
357 /* Default values */
358 if (left_padding)
359 vectors_per_line = num_vectors;
360 if (!vectors_per_line) {
361 vectors_per_line = CEIL_MUL(num_vectors / buffer_height,
362 deinterleaving);
363 line_width = 0;
364 }
365 if (!line_width)
366 line_width = vectors_per_line *
367 input_formatter_get_alignment(INPUT_FORMATTER0_ID);
368 if (!buffers_per_line)
369 buffers_per_line = deinterleaving;
370 line_width = CEIL_MUL(line_width,
371 input_formatter_get_alignment(INPUT_FORMATTER0_ID)
372 * vmem_increment);
373
374 vectors_per_buffer = buffer_height * buffer_width / ISP_VEC_NELEMS;
375
376 if_a_config.start_line = start_line;
377 if_a_config.start_column = start_column;
378 if_a_config.left_padding = left_padding / deinterleaving;
379 if_a_config.cropped_height = cropped_height;
380 if_a_config.cropped_width = width_a;
381 if_a_config.deinterleaving = deinterleaving;
382 if_a_config.buf_vecs = vectors_per_buffer;
383 if_a_config.buf_start_index = buf_offset_a;
384 if_a_config.buf_increment = vmem_increment;
385 if_a_config.buf_eol_offset =
386 buffer_width * bits_per_pixel / 8 - line_width;
387 if_a_config.is_yuv420_format =
388 (input_format == ATOMISP_INPUT_FORMAT_YUV420_8)
389 || (input_format == ATOMISP_INPUT_FORMAT_YUV420_10)
390 || (input_format == ATOMISP_INPUT_FORMAT_YUV420_16);
391 if_a_config.block_no_reqs = (config->mode != IA_CSS_INPUT_MODE_SENSOR);
392
393 if (two_ppc) {
394 if (deinterleaving_b) {
395 deinterleaving = deinterleaving_b;
396 width_b = cropped_width * deinterleaving;
397 buffer_width *= deinterleaving;
398 /* Patch from bayer to rgb */
399 num_vectors = num_vectors / 2 *
400 deinterleaving * width_b_factor;
401 vectors_per_line = num_vectors / buffer_height;
402 line_width = vectors_per_line *
403 input_formatter_get_alignment(INPUT_FORMATTER0_ID);
404 }
405 if_b_config.start_line = start_line;
406 if_b_config.start_column = start_column_b;
407 if_b_config.left_padding = left_padding / deinterleaving;
408 if_b_config.cropped_height = cropped_height;
409 if_b_config.cropped_width = width_b;
410 if_b_config.deinterleaving = deinterleaving;
411 if_b_config.buf_vecs = vectors_per_buffer;
412 if_b_config.buf_start_index = buf_offset_b;
413 if_b_config.buf_increment = vmem_increment;
414 if_b_config.buf_eol_offset =
415 buffer_width * bits_per_pixel / 8 - line_width;
416 if_b_config.is_yuv420_format =
417 input_format == ATOMISP_INPUT_FORMAT_YUV420_8
418 || input_format == ATOMISP_INPUT_FORMAT_YUV420_10
419 || input_format == ATOMISP_INPUT_FORMAT_YUV420_16;
420 if_b_config.block_no_reqs =
421 (config->mode != IA_CSS_INPUT_MODE_SENSOR);
422
423 if (if_config_index != SH_CSS_IF_CONFIG_NOT_NEEDED) {
424 assert(if_config_index <= SH_CSS_MAX_IF_CONFIGS);
425
426 ifmtr_set_if_blocking_mode(&if_a_config, &if_b_config);
427 /* Set the ifconfigs to SP group */
428 sh_css_sp_set_if_configs(&if_a_config, &if_b_config,
429 if_config_index);
430 }
431 } else {
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, NULL);
436 /* Set the ifconfigs to SP group */
437 sh_css_sp_set_if_configs(&if_a_config, NULL,
438 if_config_index);
439 }
440 }
441
442 return 0;
443 }
444
445 bool ifmtr_set_if_blocking_mode_reset = true;
446
447 /************************************************************
448 * Static functions
449 ************************************************************/
ifmtr_set_if_blocking_mode(const input_formatter_cfg_t * const config_a,const input_formatter_cfg_t * const config_b)450 static void ifmtr_set_if_blocking_mode(
451 const input_formatter_cfg_t *const config_a,
452 const input_formatter_cfg_t *const config_b)
453 {
454 int i;
455 bool block[] = { false, false, false, false };
456
457 assert(N_INPUT_FORMATTER_ID <= (ARRAY_SIZE(block)));
458
459 block[INPUT_FORMATTER0_ID] = (bool)config_a->block_no_reqs;
460 if (config_b)
461 block[INPUT_FORMATTER1_ID] = (bool)config_b->block_no_reqs;
462
463 /* TODO: next could cause issues when streams are started after
464 * eachother. */
465 /*IF should not be reconfigured/reset from host */
466 if (ifmtr_set_if_blocking_mode_reset) {
467 ifmtr_set_if_blocking_mode_reset = false;
468 for (i = 0; i < N_INPUT_FORMATTER_ID; i++) {
469 input_formatter_ID_t id = (input_formatter_ID_t)i;
470
471 input_formatter_rst(id);
472 input_formatter_set_fifo_blocking_mode(id, block[id]);
473 }
474 }
475
476 return;
477 }
478
ifmtr_start_column(const struct ia_css_stream_config * config,unsigned int bin_in,unsigned int * start_column)479 static int ifmtr_start_column(
480 const struct ia_css_stream_config *config,
481 unsigned int bin_in,
482 unsigned int *start_column)
483 {
484 unsigned int in = config->input_config.input_res.width, start,
485 for_bayer = ia_css_ifmtr_columns_needed_for_bayer_order(config);
486
487 if (bin_in + 2 * for_bayer > in)
488 return -EINVAL;
489
490 /* On the hardware, we want to use the middle of the input, so we
491 * divide the start column by 2. */
492 start = (in - bin_in) / 2;
493 /* in case the number of extra columns is 2 or odd, we round the start
494 * column down */
495 start &= ~0x1;
496
497 /* now we add the one column (if needed) to correct for the bayer
498 * order).
499 */
500 start += for_bayer;
501 *start_column = start;
502 return 0;
503 }
504
ifmtr_input_start_line(const struct ia_css_stream_config * config,unsigned int bin_in,unsigned int * start_line)505 static int ifmtr_input_start_line(
506 const struct ia_css_stream_config *config,
507 unsigned int bin_in,
508 unsigned int *start_line)
509 {
510 unsigned int in = config->input_config.input_res.height, start,
511 for_bayer = ia_css_ifmtr_lines_needed_for_bayer_order(config);
512
513 if (bin_in + 2 * for_bayer > in)
514 return -EINVAL;
515
516 /* On the hardware, we want to use the middle of the input, so we
517 * divide the start line by 2. On the simulator, we cannot handle extra
518 * lines at the end of the frame.
519 */
520 start = (in - bin_in) / 2;
521 /* in case the number of extra lines is 2 or odd, we round the start
522 * line down.
523 */
524 start &= ~0x1;
525
526 /* now we add the one line (if needed) to correct for the bayer order */
527 start += for_bayer;
528 *start_line = start;
529 return 0;
530 }
531
532