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