xref: /linux/drivers/staging/media/atomisp/pci/runtime/ifmtr/src/ifmtr.c (revision 42874e4eb35bdfc54f8514685e50434098ba4f6c)
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  */
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 
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 
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 (config->mode == IA_CSS_INPUT_MODE_TPG &&
386 	    ((binary && binary->info->sp.pipeline.mode == IA_CSS_BINARY_MODE_VIDEO) ||
387 	     (!binary))) {
388 		/* !binary -> sp raw copy pipe */
389 		/* workaround for TPG in video mode */
390 		start_line = 0;
391 		start_column = 0;
392 		cropped_height -= start_line;
393 		width_a -= start_column;
394 	}
395 
396 	if_a_config.start_line = start_line;
397 	if_a_config.start_column = start_column;
398 	if_a_config.left_padding = left_padding / deinterleaving;
399 	if_a_config.cropped_height = cropped_height;
400 	if_a_config.cropped_width = width_a;
401 	if_a_config.deinterleaving = deinterleaving;
402 	if_a_config.buf_vecs = vectors_per_buffer;
403 	if_a_config.buf_start_index = buf_offset_a;
404 	if_a_config.buf_increment = vmem_increment;
405 	if_a_config.buf_eol_offset =
406 	    buffer_width * bits_per_pixel / 8 - line_width;
407 	if_a_config.is_yuv420_format =
408 	    (input_format == ATOMISP_INPUT_FORMAT_YUV420_8)
409 	    || (input_format == ATOMISP_INPUT_FORMAT_YUV420_10)
410 	    || (input_format == ATOMISP_INPUT_FORMAT_YUV420_16);
411 	if_a_config.block_no_reqs = (config->mode != IA_CSS_INPUT_MODE_SENSOR);
412 
413 	if (two_ppc) {
414 		if (deinterleaving_b) {
415 			deinterleaving = deinterleaving_b;
416 			width_b = cropped_width * deinterleaving;
417 			buffer_width *= deinterleaving;
418 			/* Patch from bayer to rgb */
419 			num_vectors = num_vectors / 2 *
420 				      deinterleaving * width_b_factor;
421 			vectors_per_line = num_vectors / buffer_height;
422 			line_width = vectors_per_line *
423 				     input_formatter_get_alignment(INPUT_FORMATTER0_ID);
424 		}
425 		if_b_config.start_line = start_line;
426 		if_b_config.start_column = start_column_b;
427 		if_b_config.left_padding = left_padding / deinterleaving;
428 		if_b_config.cropped_height = cropped_height;
429 		if_b_config.cropped_width = width_b;
430 		if_b_config.deinterleaving = deinterleaving;
431 		if_b_config.buf_vecs = vectors_per_buffer;
432 		if_b_config.buf_start_index = buf_offset_b;
433 		if_b_config.buf_increment = vmem_increment;
434 		if_b_config.buf_eol_offset =
435 		    buffer_width * bits_per_pixel / 8 - line_width;
436 		if_b_config.is_yuv420_format =
437 		    input_format == ATOMISP_INPUT_FORMAT_YUV420_8
438 		    || input_format == ATOMISP_INPUT_FORMAT_YUV420_10
439 		    || input_format == ATOMISP_INPUT_FORMAT_YUV420_16;
440 		if_b_config.block_no_reqs =
441 		    (config->mode != IA_CSS_INPUT_MODE_SENSOR);
442 
443 		if (if_config_index != SH_CSS_IF_CONFIG_NOT_NEEDED) {
444 			assert(if_config_index <= SH_CSS_MAX_IF_CONFIGS);
445 
446 			ifmtr_set_if_blocking_mode(&if_a_config, &if_b_config);
447 			/* Set the ifconfigs to SP group */
448 			sh_css_sp_set_if_configs(&if_a_config, &if_b_config,
449 						 if_config_index);
450 		}
451 	} else {
452 		if (if_config_index != SH_CSS_IF_CONFIG_NOT_NEEDED) {
453 			assert(if_config_index <= SH_CSS_MAX_IF_CONFIGS);
454 
455 			ifmtr_set_if_blocking_mode(&if_a_config, NULL);
456 			/* Set the ifconfigs to SP group */
457 			sh_css_sp_set_if_configs(&if_a_config, NULL,
458 						 if_config_index);
459 		}
460 	}
461 
462 	return 0;
463 }
464 
465 bool ifmtr_set_if_blocking_mode_reset = true;
466 
467 /************************************************************
468  * Static functions
469  ************************************************************/
470 static void ifmtr_set_if_blocking_mode(
471     const input_formatter_cfg_t *const config_a,
472     const input_formatter_cfg_t *const config_b)
473 {
474 	int i;
475 	bool block[] = { false, false, false, false };
476 
477 	assert(N_INPUT_FORMATTER_ID <= (ARRAY_SIZE(block)));
478 
479 	block[INPUT_FORMATTER0_ID] = (bool)config_a->block_no_reqs;
480 	if (config_b)
481 		block[INPUT_FORMATTER1_ID] = (bool)config_b->block_no_reqs;
482 
483 	/* TODO: next could cause issues when streams are started after
484 	 * eachother. */
485 	/*IF should not be reconfigured/reset from host */
486 	if (ifmtr_set_if_blocking_mode_reset) {
487 		ifmtr_set_if_blocking_mode_reset = false;
488 		for (i = 0; i < N_INPUT_FORMATTER_ID; i++) {
489 			input_formatter_ID_t id = (input_formatter_ID_t)i;
490 
491 			input_formatter_rst(id);
492 			input_formatter_set_fifo_blocking_mode(id, block[id]);
493 		}
494 	}
495 
496 	return;
497 }
498 
499 static int ifmtr_start_column(
500     const struct ia_css_stream_config *config,
501     unsigned int bin_in,
502     unsigned int *start_column)
503 {
504 	unsigned int in = config->input_config.input_res.width, start,
505 		     for_bayer = ia_css_ifmtr_columns_needed_for_bayer_order(config);
506 
507 	if (bin_in + 2 * for_bayer > in)
508 		return -EINVAL;
509 
510 	/* On the hardware, we want to use the middle of the input, so we
511 	 * divide the start column by 2. */
512 	start = (in - bin_in) / 2;
513 	/* in case the number of extra columns is 2 or odd, we round the start
514 	 * column down */
515 	start &= ~0x1;
516 
517 	/* now we add the one column (if needed) to correct for the bayer
518 	 * order).
519 	 */
520 	start += for_bayer;
521 	*start_column = start;
522 	return 0;
523 }
524 
525 static int ifmtr_input_start_line(
526     const struct ia_css_stream_config *config,
527     unsigned int bin_in,
528     unsigned int *start_line)
529 {
530 	unsigned int in = config->input_config.input_res.height, start,
531 		     for_bayer = ia_css_ifmtr_lines_needed_for_bayer_order(config);
532 
533 	if (bin_in + 2 * for_bayer > in)
534 		return -EINVAL;
535 
536 	/* On the hardware, we want to use the middle of the input, so we
537 	 * divide the start line by 2. On the simulator, we cannot handle extra
538 	 * lines at the end of the frame.
539 	 */
540 	start = (in - bin_in) / 2;
541 	/* in case the number of extra lines is 2 or odd, we round the start
542 	 * line down.
543 	 */
544 	start &= ~0x1;
545 
546 	/* now we add the one line (if needed) to correct for the bayer order */
547 	start += for_bayer;
548 	*start_line = start;
549 	return 0;
550 }
551 
552