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