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