1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Support for Intel Camera Imaging ISP subsystem.
4  * Copyright (c) 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 "ia_css_mipi.h"
17 #include "sh_css_mipi.h"
18 #include <type_support.h>
19 #include "system_global.h"
20 #include "ia_css_err.h"
21 #include "ia_css_pipe.h"
22 #include "ia_css_stream_format.h"
23 #include "sh_css_stream_format.h"
24 #include "ia_css_stream_public.h"
25 #include "ia_css_frame_public.h"
26 #include "ia_css_input_port.h"
27 #include "ia_css_debug.h"
28 #include "sh_css_struct.h"
29 #include "sh_css_defs.h"
30 #include "sh_css_sp.h" /* sh_css_update_host2sp_mipi_frame sh_css_update_host2sp_num_mipi_frames ... */
31 #include "sw_event_global.h" /* IA_CSS_PSYS_SW_EVENT_MIPI_BUFFERS_READY */
32 
33 static u32
34 ref_count_mipi_allocation[N_CSI_PORTS]; /* Initialized in mipi_init */
35 
36 /*
37  * Check if a source port or TPG/PRBS ID is valid
38  */
39 static bool ia_css_mipi_is_source_port_valid(struct ia_css_pipe *pipe,
40 	unsigned int *pport)
41 {
42 	bool ret = true;
43 	unsigned int port = 0;
44 	unsigned int max_ports = 0;
45 
46 	switch (pipe->stream->config.mode) {
47 	case IA_CSS_INPUT_MODE_BUFFERED_SENSOR:
48 		port = (unsigned int)pipe->stream->config.source.port.port;
49 		max_ports = N_CSI_PORTS;
50 		break;
51 	case IA_CSS_INPUT_MODE_TPG:
52 		port = (unsigned int)pipe->stream->config.source.tpg.id;
53 		max_ports = N_CSS_TPG_IDS;
54 		break;
55 	case IA_CSS_INPUT_MODE_PRBS:
56 		port = (unsigned int)pipe->stream->config.source.prbs.id;
57 		max_ports = N_CSS_PRBS_IDS;
58 		break;
59 	default:
60 		assert(false);
61 		ret = false;
62 		break;
63 	}
64 
65 	if (ret) {
66 		assert(port < max_ports);
67 
68 		if (port >= max_ports)
69 			ret = false;
70 	}
71 
72 	*pport = port;
73 
74 	return ret;
75 }
76 
77 /* Assumptions:
78  *	- A line is multiple of 4 bytes = 1 word.
79  *	- Each frame has SOF and EOF (each 1 word).
80  *	- Each line has format header and optionally SOL and EOL (each 1 word).
81  *	- Odd and even lines of YUV420 format are different in bites per pixel size.
82  *	- Custom size of embedded data.
83  *  -- Interleaved frames are not taken into account.
84  *  -- Lines are multiples of 8B, and not necessary of (custom 3B, or 7B
85  *  etc.).
86  * Result is given in DDR mem words, 32B or 256 bits
87  */
88 int
89 ia_css_mipi_frame_calculate_size(const unsigned int width,
90 				 const unsigned int height,
91 				 const enum atomisp_input_format format,
92 				 const bool hasSOLandEOL,
93 				 const unsigned int embedded_data_size_words,
94 				 unsigned int *size_mem_words)
95 {
96 	int err = 0;
97 
98 	unsigned int bits_per_pixel = 0;
99 	unsigned int even_line_bytes = 0;
100 	unsigned int odd_line_bytes = 0;
101 	unsigned int words_per_odd_line = 0;
102 	unsigned int words_for_first_line = 0;
103 	unsigned int words_per_even_line = 0;
104 	unsigned int mem_words_per_even_line = 0;
105 	unsigned int mem_words_per_odd_line = 0;
106 	unsigned int mem_words_for_first_line = 0;
107 	unsigned int mem_words_for_EOF = 0;
108 	unsigned int mem_words = 0;
109 	unsigned int width_padded = width;
110 
111 #if defined(ISP2401)
112 	/* The changes will be reverted as soon as RAW
113 	 * Buffers are deployed by the 2401 Input System
114 	 * in the non-continuous use scenario.
115 	 */
116 	width_padded += (2 * ISP_VEC_NELEMS);
117 #endif
118 
119 	IA_CSS_ENTER("padded_width=%d, height=%d, format=%d, hasSOLandEOL=%d, embedded_data_size_words=%d\n",
120 		     width_padded, height, format, hasSOLandEOL, embedded_data_size_words);
121 
122 	switch (format) {
123 	case ATOMISP_INPUT_FORMAT_RAW_6:		/* 4p, 3B, 24bits */
124 		bits_per_pixel = 6;
125 		break;
126 	case ATOMISP_INPUT_FORMAT_RAW_7:		/* 8p, 7B, 56bits */
127 		bits_per_pixel = 7;
128 		break;
129 	case ATOMISP_INPUT_FORMAT_RAW_8:		/* 1p, 1B, 8bits */
130 	case ATOMISP_INPUT_FORMAT_BINARY_8:		/*  8bits, TODO: check. */
131 	case ATOMISP_INPUT_FORMAT_YUV420_8:		/* odd 2p, 2B, 16bits, even 2p, 4B, 32bits */
132 		bits_per_pixel = 8;
133 		break;
134 	case ATOMISP_INPUT_FORMAT_YUV420_10:		/* odd 4p, 5B, 40bits, even 4p, 10B, 80bits */
135 	case ATOMISP_INPUT_FORMAT_RAW_10:		/* 4p, 5B, 40bits */
136 #if !defined(HAS_NO_PACKED_RAW_PIXELS)
137 		/* The changes will be reverted as soon as RAW
138 		 * Buffers are deployed by the 2401 Input System
139 		 * in the non-continuous use scenario.
140 		 */
141 		bits_per_pixel = 10;
142 #else
143 		bits_per_pixel = 16;
144 #endif
145 		break;
146 	case ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY:	/* 2p, 3B, 24bits */
147 	case ATOMISP_INPUT_FORMAT_RAW_12:		/* 2p, 3B, 24bits */
148 		bits_per_pixel = 12;
149 		break;
150 	case ATOMISP_INPUT_FORMAT_RAW_14:		/* 4p, 7B, 56bits */
151 		bits_per_pixel = 14;
152 		break;
153 	case ATOMISP_INPUT_FORMAT_RGB_444:		/* 1p, 2B, 16bits */
154 	case ATOMISP_INPUT_FORMAT_RGB_555:		/* 1p, 2B, 16bits */
155 	case ATOMISP_INPUT_FORMAT_RGB_565:		/* 1p, 2B, 16bits */
156 	case ATOMISP_INPUT_FORMAT_YUV422_8:		/* 2p, 4B, 32bits */
157 		bits_per_pixel = 16;
158 		break;
159 	case ATOMISP_INPUT_FORMAT_RGB_666:		/* 4p, 9B, 72bits */
160 		bits_per_pixel = 18;
161 		break;
162 	case ATOMISP_INPUT_FORMAT_YUV422_10:		/* 2p, 5B, 40bits */
163 		bits_per_pixel = 20;
164 		break;
165 	case ATOMISP_INPUT_FORMAT_RGB_888:		/* 1p, 3B, 24bits */
166 		bits_per_pixel = 24;
167 		break;
168 
169 	case ATOMISP_INPUT_FORMAT_YUV420_16:		/* Not supported */
170 	case ATOMISP_INPUT_FORMAT_YUV422_16:		/* Not supported */
171 	case ATOMISP_INPUT_FORMAT_RAW_16:		/* TODO: not specified in MIPI SPEC, check */
172 	default:
173 		return -EINVAL;
174 	}
175 
176 	odd_line_bytes = (width_padded * bits_per_pixel + 7) >> 3; /* ceil ( bits per line / 8) */
177 
178 	/* Even lines for YUV420 formats are double in bits_per_pixel. */
179 	if (format == ATOMISP_INPUT_FORMAT_YUV420_8
180 	    || format == ATOMISP_INPUT_FORMAT_YUV420_10
181 	    || format == ATOMISP_INPUT_FORMAT_YUV420_16) {
182 		even_line_bytes = (width_padded * 2 * bits_per_pixel + 7) >>
183 			3; /* ceil ( bits per line / 8) */
184 	} else {
185 		even_line_bytes = odd_line_bytes;
186 	}
187 
188 	/*  a frame represented in memory:  ()- optional; data - payload words.
189 	*  addr		0	1	2	3	4	5	6	7:
190 	*  first	SOF	(SOL)	PACK_H	data	data	data	data	data
191 	*		data	data	data	data	data	data	data	data
192 	*		...
193 	*		data	data	0	0	0	0	0	0
194 	*  second	(EOL)	(SOL)	PACK_H	data	data	data	data	data
195 	*		data	data	data	data	data	data	data	data
196 	*		...
197 	*		data	data	0	0	0	0	0	0
198 	*  ...
199 	*  last		(EOL)	EOF	0	0	0	0	0	0
200 	*
201 	*  Embedded lines are regular lines stored before the first and after
202 	*  payload lines.
203 	*/
204 
205 	words_per_odd_line = (odd_line_bytes + 3) >> 2;
206 	/* ceil(odd_line_bytes/4); word = 4 bytes */
207 	words_per_even_line  = (even_line_bytes  + 3) >> 2;
208 	words_for_first_line = words_per_odd_line + 2 + (hasSOLandEOL ? 1 : 0);
209 	/* + SOF +packet header + optionally (SOL), but (EOL) is not in the first line */
210 	words_per_odd_line	+= (1 + (hasSOLandEOL ? 2 : 0));
211 	/* each non-first line has format header, and optionally (SOL) and (EOL). */
212 	words_per_even_line += (1 + (hasSOLandEOL ? 2 : 0));
213 
214 	mem_words_per_odd_line	 = (words_per_odd_line + 7) >> 3;
215 	/* ceil(words_per_odd_line/8); mem_word = 32 bytes, 8 words */
216 	mem_words_for_first_line = (words_for_first_line + 7) >> 3;
217 	mem_words_per_even_line  = (words_per_even_line + 7) >> 3;
218 	mem_words_for_EOF        = 1; /* last line consisit of the optional (EOL) and EOF */
219 
220 	mem_words = ((embedded_data_size_words + 7) >> 3) +
221 	mem_words_for_first_line +
222 	(((height + 1) >> 1) - 1) * mem_words_per_odd_line +
223 	/* ceil (height/2) - 1 (first line is calculated separatelly) */
224 	(height      >> 1) * mem_words_per_even_line + /* floor(height/2) */
225 	mem_words_for_EOF;
226 
227 	*size_mem_words = mem_words; /* ceil(words/8); mem word is 32B = 8words. */
228 	/* Check if the above is still needed. */
229 
230 	IA_CSS_LEAVE_ERR(err);
231 	return err;
232 }
233 
234 #if !defined(ISP2401)
235 int
236 ia_css_mipi_frame_enable_check_on_size(const enum mipi_port_id port,
237 				       const unsigned int	size_mem_words)
238 {
239 	u32 idx;
240 
241 	int err = -EBUSY;
242 
243 	OP___assert(port < N_CSI_PORTS);
244 	OP___assert(size_mem_words != 0);
245 
246 	for (idx = 0; idx < IA_CSS_MIPI_SIZE_CHECK_MAX_NOF_ENTRIES_PER_PORT &&
247 	     my_css.mipi_sizes_for_check[port][idx] != 0;
248 	     idx++) { /* do nothing */
249 	}
250 	if (idx < IA_CSS_MIPI_SIZE_CHECK_MAX_NOF_ENTRIES_PER_PORT) {
251 		my_css.mipi_sizes_for_check[port][idx] = size_mem_words;
252 		err = 0;
253 	}
254 
255 	return err;
256 }
257 #endif
258 
259 void
260 mipi_init(void)
261 {
262 	unsigned int i;
263 
264 	for (i = 0; i < N_CSI_PORTS; i++)
265 		ref_count_mipi_allocation[i] = 0;
266 }
267 
268 int
269 calculate_mipi_buff_size(
270     struct ia_css_stream_config *stream_cfg,
271     unsigned int *size_mem_words)
272 {
273 #if !defined(ISP2401)
274 	int err = -EINVAL;
275 	(void)stream_cfg;
276 	(void)size_mem_words;
277 #else
278 	unsigned int width;
279 	unsigned int height;
280 	enum atomisp_input_format format;
281 	bool pack_raw_pixels;
282 
283 	unsigned int width_padded;
284 	unsigned int bits_per_pixel = 0;
285 
286 	unsigned int even_line_bytes = 0;
287 	unsigned int odd_line_bytes = 0;
288 
289 	unsigned int words_per_odd_line = 0;
290 	unsigned int words_per_even_line = 0;
291 
292 	unsigned int mem_words_per_even_line = 0;
293 	unsigned int mem_words_per_odd_line = 0;
294 
295 	unsigned int mem_words_per_buff_line = 0;
296 	unsigned int mem_words_per_buff = 0;
297 	int err = 0;
298 
299 	/**
300 	 * zhengjie.lu@intel.com
301 	 *
302 	 * NOTE
303 	 * - In the struct "ia_css_stream_config", there
304 	 *   are two members: "input_config" and "isys_config".
305 	 *   Both of them provide the same information, e.g.
306 	 *   input_res and format.
307 	 *
308 	 *   Question here is that: which one shall be used?
309 	 */
310 	width = stream_cfg->input_config.input_res.width;
311 	height = stream_cfg->input_config.input_res.height;
312 	format = stream_cfg->input_config.format;
313 	pack_raw_pixels = stream_cfg->pack_raw_pixels;
314 	/* end of NOTE */
315 
316 	/**
317 	 * zhengjie.lu@intel.com
318 	 *
319 	 * NOTE
320 	 * - The following code is derived from the
321 	 *   existing code "ia_css_mipi_frame_calculate_size()".
322 	 *
323 	 *   Question here is: why adding "2 * ISP_VEC_NELEMS"
324 	 *   to "width_padded", but not making "width_padded"
325 	 *   aligned with "2 * ISP_VEC_NELEMS"?
326 	 */
327 	/* The changes will be reverted as soon as RAW
328 	 * Buffers are deployed by the 2401 Input System
329 	 * in the non-continuous use scenario.
330 	 */
331 	width_padded = width + (2 * ISP_VEC_NELEMS);
332 	/* end of NOTE */
333 
334 	IA_CSS_ENTER("padded_width=%d, height=%d, format=%d\n",
335 		     width_padded, height, format);
336 
337 	bits_per_pixel = sh_css_stream_format_2_bits_per_subpixel(format);
338 	bits_per_pixel =
339 	(format == ATOMISP_INPUT_FORMAT_RAW_10 && pack_raw_pixels) ? bits_per_pixel : 16;
340 	if (bits_per_pixel == 0)
341 		return -EINVAL;
342 
343 	odd_line_bytes = (width_padded * bits_per_pixel + 7) >> 3; /* ceil ( bits per line / 8) */
344 
345 	/* Even lines for YUV420 formats are double in bits_per_pixel. */
346 	if (format == ATOMISP_INPUT_FORMAT_YUV420_8
347 	    || format == ATOMISP_INPUT_FORMAT_YUV420_10) {
348 		even_line_bytes = (width_padded * 2 * bits_per_pixel + 7) >>
349 			3; /* ceil ( bits per line / 8) */
350 	} else {
351 		even_line_bytes = odd_line_bytes;
352 	}
353 
354 	words_per_odd_line	 = (odd_line_bytes   + 3) >> 2;
355 	/* ceil(odd_line_bytes/4); word = 4 bytes */
356 	words_per_even_line  = (even_line_bytes  + 3) >> 2;
357 
358 	mem_words_per_odd_line	 = (words_per_odd_line + 7) >> 3;
359 	/* ceil(words_per_odd_line/8); mem_word = 32 bytes, 8 words */
360 	mem_words_per_even_line  = (words_per_even_line + 7) >> 3;
361 
362 	mem_words_per_buff_line =
363 	(mem_words_per_odd_line > mem_words_per_even_line) ? mem_words_per_odd_line : mem_words_per_even_line;
364 	mem_words_per_buff = mem_words_per_buff_line * height;
365 
366 	*size_mem_words = mem_words_per_buff;
367 
368 	IA_CSS_LEAVE_ERR(err);
369 #endif
370 	return err;
371 }
372 
373 static bool buffers_needed(struct ia_css_pipe *pipe)
374 {
375 	if (!IS_ISP2401) {
376 		if (pipe->stream->config.mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR)
377 			return false;
378 		else
379 			return true;
380 	}
381 
382 	if (pipe->stream->config.mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR ||
383 	    pipe->stream->config.mode == IA_CSS_INPUT_MODE_TPG ||
384 	    pipe->stream->config.mode == IA_CSS_INPUT_MODE_PRBS)
385 		return false;
386 
387 	return true;
388 }
389 
390 int
391 allocate_mipi_frames(struct ia_css_pipe *pipe,
392 		     struct ia_css_stream_info *info)
393 {
394 	int err = -EINVAL;
395 	unsigned int port;
396 
397 	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
398 			    "allocate_mipi_frames(%p) enter:\n", pipe);
399 
400 	assert(pipe);
401 	assert(pipe->stream);
402 	if ((!pipe) || (!pipe->stream)) {
403 		ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
404 				    "allocate_mipi_frames(%p) exit: pipe or stream is null.\n",
405 				    pipe);
406 		return -EINVAL;
407 	}
408 
409 #ifdef ISP2401
410 	if (pipe->stream->config.online) {
411 		ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
412 				    "allocate_mipi_frames(%p) exit: no buffers needed for 2401 pipe mode.\n",
413 				    pipe);
414 		return 0;
415 	}
416 
417 #endif
418 
419 	if (!buffers_needed(pipe)) {
420 		ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
421 				    "allocate_mipi_frames(%p) exit: no buffers needed for pipe mode.\n",
422 				    pipe);
423 		return 0; /* AM TODO: Check  */
424 	}
425 
426 	if (!IS_ISP2401)
427 		port = (unsigned int)pipe->stream->config.source.port.port;
428 	else
429 		err = ia_css_mipi_is_source_port_valid(pipe, &port);
430 
431 	assert(port < N_CSI_PORTS);
432 
433 	if (port >= N_CSI_PORTS || err) {
434 		ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
435 				    "allocate_mipi_frames(%p) exit: error: port is not correct (port=%d).\n",
436 				    pipe, port);
437 		return -EINVAL;
438 	}
439 
440 #ifdef ISP2401
441 	err = calculate_mipi_buff_size(
442 	    &pipe->stream->config,
443 	    &my_css.mipi_frame_size[port]);
444 #endif
445 
446 #if !defined(ISP2401)
447 	if (ref_count_mipi_allocation[port] != 0) {
448 		ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
449 				    "allocate_mipi_frames(%p) exit: already allocated for this port (port=%d).\n",
450 				    pipe, port);
451 		return 0;
452 	}
453 #else
454 	/* 2401 system allows multiple streams to use same physical port. This is not
455 	 * true for 2400 system. Currently 2401 uses MIPI buffers as a temporary solution.
456 	 * TODO AM: Once that is changed (removed) this code should be removed as well.
457 	 * In that case only 2400 related code should remain.
458 	 */
459 	if (ref_count_mipi_allocation[port] != 0) {
460 		ref_count_mipi_allocation[port]++;
461 		ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
462 				    "allocate_mipi_frames(%p) leave: nothing to do, already allocated for this port (port=%d).\n",
463 				    pipe, port);
464 		return 0;
465 	}
466 #endif
467 
468 	ref_count_mipi_allocation[port]++;
469 
470 	/* AM TODO: mipi frames number should come from stream struct. */
471 	my_css.num_mipi_frames[port] = NUM_MIPI_FRAMES_PER_STREAM;
472 
473 	/* Incremental allocation (per stream), not for all streams at once. */
474 	{ /* limit the scope of i,j */
475 		unsigned int i, j;
476 
477 		for (i = 0; i < my_css.num_mipi_frames[port]; i++) {
478 			/* free previous frame */
479 			if (my_css.mipi_frames[port][i]) {
480 				ia_css_frame_free(my_css.mipi_frames[port][i]);
481 				my_css.mipi_frames[port][i] = NULL;
482 			}
483 			/* check if new frame is needed */
484 			if (i < my_css.num_mipi_frames[port]) {
485 				/* allocate new frame */
486 				err = ia_css_frame_allocate_with_buffer_size(
487 					  &my_css.mipi_frames[port][i],
488 					  my_css.mipi_frame_size[port] * HIVE_ISP_DDR_WORD_BYTES,
489 					  false);
490 				if (err) {
491 					for (j = 0; j < i; j++) {
492 						if (my_css.mipi_frames[port][j]) {
493 							ia_css_frame_free(my_css.mipi_frames[port][j]);
494 							my_css.mipi_frames[port][j] = NULL;
495 						}
496 					}
497 					ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
498 							    "allocate_mipi_frames(%p, %d) exit: error: allocation failed.\n",
499 							    pipe, port);
500 					return err;
501 				}
502 			}
503 			if (info->metadata_info.size > 0) {
504 				/* free previous metadata buffer */
505 				if (my_css.mipi_metadata[port][i]) {
506 					ia_css_metadata_free(my_css.mipi_metadata[port][i]);
507 					my_css.mipi_metadata[port][i] = NULL;
508 				}
509 				/* check if need to allocate a new metadata buffer */
510 				if (i < my_css.num_mipi_frames[port]) {
511 					/* allocate new metadata buffer */
512 					my_css.mipi_metadata[port][i] = ia_css_metadata_allocate(&info->metadata_info);
513 					if (!my_css.mipi_metadata[port][i]) {
514 						ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
515 								    "allocate_mipi_metadata(%p, %d) failed.\n",
516 								    pipe, port);
517 						return err;
518 					}
519 				}
520 			}
521 		}
522 	}
523 	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
524 			    "allocate_mipi_frames(%p) exit:\n", pipe);
525 
526 	return err;
527 }
528 
529 int
530 free_mipi_frames(struct ia_css_pipe *pipe)
531 {
532 	int err = -EINVAL;
533 	unsigned int port;
534 
535 	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
536 			    "free_mipi_frames(%p) enter:\n", pipe);
537 
538 	/* assert(pipe != NULL); TEMP: TODO: Should be assert only. */
539 	if (pipe) {
540 		assert(pipe->stream);
541 		if ((!pipe) || (!pipe->stream)) {
542 			ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
543 					    "free_mipi_frames(%p) exit: error: pipe or stream is null.\n",
544 					    pipe);
545 			return -EINVAL;
546 		}
547 
548 		if (!buffers_needed(pipe)) {
549 			ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
550 					    "free_mipi_frames(%p) exit: error: wrong mode.\n",
551 					    pipe);
552 			return err;
553 		}
554 
555 		if (!IS_ISP2401)
556 			port = (unsigned int)pipe->stream->config.source.port.port;
557 		else
558 			err = ia_css_mipi_is_source_port_valid(pipe, &port);
559 
560 		assert(port < N_CSI_PORTS);
561 
562 		if (port >= N_CSI_PORTS || err) {
563 			ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
564 					    "free_mipi_frames(%p, %d) exit: error: pipe port is not correct.\n",
565 					    pipe, port);
566 			return err;
567 		}
568 
569 		if (ref_count_mipi_allocation[port] > 0) {
570 #if !defined(ISP2401)
571 			assert(ref_count_mipi_allocation[port] == 1);
572 			if (ref_count_mipi_allocation[port] != 1) {
573 				ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
574 						    "free_mipi_frames(%p) exit: error: wrong ref_count (ref_count=%d).\n",
575 						    pipe, ref_count_mipi_allocation[port]);
576 				return err;
577 			}
578 #endif
579 
580 			ref_count_mipi_allocation[port]--;
581 
582 			if (ref_count_mipi_allocation[port] == 0) {
583 				/* no streams are using this buffer, so free it */
584 				unsigned int i;
585 
586 				for (i = 0; i < my_css.num_mipi_frames[port]; i++) {
587 					if (my_css.mipi_frames[port][i]) {
588 						ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
589 								    "free_mipi_frames(port=%d, num=%d).\n", port, i);
590 						ia_css_frame_free(my_css.mipi_frames[port][i]);
591 						my_css.mipi_frames[port][i] = NULL;
592 					}
593 					if (my_css.mipi_metadata[port][i]) {
594 						ia_css_metadata_free(my_css.mipi_metadata[port][i]);
595 						my_css.mipi_metadata[port][i] = NULL;
596 					}
597 				}
598 
599 				ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
600 						    "free_mipi_frames(%p) exit (deallocated).\n", pipe);
601 			}
602 #if defined(ISP2401)
603 			else {
604 				/* 2401 system allows multiple streams to use same physical port. This is not
605 				 * true for 2400 system. Currently 2401 uses MIPI buffers as a temporary solution.
606 				 * TODO AM: Once that is changed (removed) this code should be removed as well.
607 				 * In that case only 2400 related code should remain.
608 				 */
609 				ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
610 						    "free_mipi_frames(%p) leave: nothing to do, other streams still use this port (port=%d).\n",
611 						    pipe, port);
612 			}
613 #endif
614 		}
615 	} else { /* pipe ==NULL */
616 		/* AM TEMP: free-ing all mipi buffers just like a legacy code. */
617 		for (port = CSI_PORT0_ID; port < N_CSI_PORTS; port++) {
618 			unsigned int i;
619 
620 			for (i = 0; i < my_css.num_mipi_frames[port]; i++) {
621 				if (my_css.mipi_frames[port][i]) {
622 					ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
623 							    "free_mipi_frames(port=%d, num=%d).\n", port, i);
624 					ia_css_frame_free(my_css.mipi_frames[port][i]);
625 					my_css.mipi_frames[port][i] = NULL;
626 				}
627 				if (my_css.mipi_metadata[port][i]) {
628 					ia_css_metadata_free(my_css.mipi_metadata[port][i]);
629 					my_css.mipi_metadata[port][i] = NULL;
630 				}
631 			}
632 			ref_count_mipi_allocation[port] = 0;
633 		}
634 	}
635 	return 0;
636 }
637 
638 int
639 send_mipi_frames(struct ia_css_pipe *pipe)
640 {
641 	int err = -EINVAL;
642 	unsigned int i;
643 #ifndef ISP2401
644 	unsigned int port;
645 #else
646 	unsigned int port = 0;
647 #endif
648 
649 	IA_CSS_ENTER_PRIVATE("pipe=%p", pipe);
650 
651 	assert(pipe);
652 	assert(pipe->stream);
653 	if (!pipe || !pipe->stream) {
654 		IA_CSS_ERROR("pipe or stream is null");
655 		return -EINVAL;
656 	}
657 
658 	/* multi stream video needs mipi buffers */
659 	/* nothing to be done in other cases. */
660 	if (!buffers_needed(pipe)) {
661 		IA_CSS_LOG("nothing to be done for this mode");
662 		return 0;
663 		/* TODO: AM: maybe this should be returning an error. */
664 	}
665 
666 	if (!IS_ISP2401)
667 		port = (unsigned int)pipe->stream->config.source.port.port;
668 	else
669 		err = ia_css_mipi_is_source_port_valid(pipe, &port);
670 
671 	assert(port < N_CSI_PORTS);
672 
673 	if (port >= N_CSI_PORTS || err) {
674 		IA_CSS_ERROR("send_mipi_frames(%p) exit: invalid port specified (port=%d).\n",
675 			     pipe, port);
676 		return err;
677 	}
678 
679 	/* Hand-over the SP-internal mipi buffers */
680 	for (i = 0; i < my_css.num_mipi_frames[port]; i++) {
681 		/* Need to include the ofset for port. */
682 		sh_css_update_host2sp_mipi_frame(port * NUM_MIPI_FRAMES_PER_STREAM + i,
683 						 my_css.mipi_frames[port][i]);
684 		sh_css_update_host2sp_mipi_metadata(port * NUM_MIPI_FRAMES_PER_STREAM + i,
685 						    my_css.mipi_metadata[port][i]);
686 	}
687 	sh_css_update_host2sp_num_mipi_frames(my_css.num_mipi_frames[port]);
688 
689 	/**********************************
690 	 * Send an event to inform the SP
691 	 * that all MIPI frames are passed.
692 	 **********************************/
693 	if (!sh_css_sp_is_running()) {
694 		/* SP is not running. The queues are not valid */
695 		IA_CSS_ERROR("sp is not running");
696 		return err;
697 	}
698 
699 	ia_css_bufq_enqueue_psys_event(
700 	    IA_CSS_PSYS_SW_EVENT_MIPI_BUFFERS_READY,
701 	    (uint8_t)port,
702 	    (uint8_t)my_css.num_mipi_frames[port],
703 	    0 /* not used */);
704 	IA_CSS_LEAVE_ERR_PRIVATE(0);
705 	return 0;
706 }
707