xref: /linux/drivers/staging/media/atomisp/pci/runtime/ifmtr/src/ifmtr.c (revision 0cdee263bc5e7b20f657ea09f9272f50c568f35b)
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