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