1 /*
2 * Copyright 2017-2021 NXP
3 * All rights reserved.
4 *
5 *
6 * SPDX-License-Identifier: BSD-3-Clause
7 */
8
9 #include "fsl_csi.h"
10 #if CSI_DRIVER_FRAG_MODE
11 #include "fsl_cache.h"
12 #endif
13
14 #if defined(FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET) && FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET
15 #include "fsl_memory.h"
16 #endif
17
18 /*******************************************************************************
19 * Definitions
20 ******************************************************************************/
21 /* Macro remap. */
22 #if (!defined(CSI_CR3_TWO_8BIT_SENSOR_MASK) && defined(CSI_CR3_SENSOR_16BITS_MASK))
23 #define CSI_CR3_TWO_8BIT_SENSOR_MASK CSI_CR3_SENSOR_16BITS_MASK
24 #endif
25
26 /* Component ID definition, used by tools. */
27 #ifndef FSL_COMPONENT_ID
28 #define FSL_COMPONENT_ID "platform.drivers.csi"
29 #endif
30
31 /* Two frame buffer loaded to CSI register at most. */
32 #define CSI_MAX_ACTIVE_FRAME_NUM 2U
33
34 /* CSI driver only support RGB565 and YUV422 in fragment mode, 2 bytes per pixel. */
35 #define CSI_FRAG_INPUT_BYTES_PER_PIXEL 2U
36
37 #if defined(FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET) && FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET
38 #define CSI_ADDR_CPU_2_IP(addr) (MEMORY_ConvertMemoryMapAddress((uint32_t)(addr), kMEMORY_Local2DMA))
39 #define CSI_ADDR_IP_2_CPU(addr) (MEMORY_ConvertMemoryMapAddress((uint32_t)(addr), kMEMORY_DMA2Local))
40 #else
41 #define CSI_ADDR_CPU_2_IP(addr) (addr)
42 #define CSI_ADDR_IP_2_CPU(addr) (addr)
43 #endif /* FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET */
44
45 /*!
46 * @brief Used for conversion between `void*` and `uint32_t`.
47 */
48 typedef union pvoid_to_u32
49 {
50 void *pvoid;
51 uint32_t u32;
52 } pvoid_to_u32_t;
53
54 /*******************************************************************************
55 * Prototypes
56 ******************************************************************************/
57
58 /*!
59 * @brief Get the instance from the base address
60 *
61 * @param base CSI peripheral base address
62 *
63 * @return The CSI module instance
64 */
65 static uint32_t CSI_GetInstance(CSI_Type *base);
66
67 #if !CSI_DRIVER_FRAG_MODE
68
69 /*!
70 * @brief Get the RX frame buffer address.
71 *
72 * @param base CSI peripheral base address.
73 * @param index Buffer index.
74 * @return Frame buffer address.
75 */
76 static uint32_t CSI_GetRxBufferAddr(CSI_Type *base, uint8_t index);
77
78 /* Typedef for interrupt handler. */
79 typedef void (*csi_isr_t)(CSI_Type *base, csi_handle_t *handle);
80
81 #else
82
83 /* Typedef for interrupt handler to work in fragment mode. */
84 typedef void (*csi_isr_t)(CSI_Type *base, csi_frag_handle_t *handle);
85 #endif /* CSI_DRIVER_FRAG_MODE */
86
87 /*******************************************************************************
88 * Variables
89 ******************************************************************************/
90 /*! @brief Pointers to CSI bases for each instance. */
91 static CSI_Type *const s_csiBases[] = CSI_BASE_PTRS;
92
93 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
94 /*! @brief Pointers to CSI clocks for each CSI submodule. */
95 static const clock_ip_name_t s_csiClocks[] = CSI_CLOCKS;
96 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
97
98 /* Array for the CSI driver handle. */
99 #if !CSI_DRIVER_FRAG_MODE
100 static csi_handle_t *s_csiHandle[ARRAY_SIZE(s_csiBases)];
101 #else
102 static csi_frag_handle_t *s_csiHandle[ARRAY_SIZE(s_csiBases)];
103 #endif
104
105 /* Array of CSI IRQ number. */
106 static const IRQn_Type s_csiIRQ[] = CSI_IRQS;
107
108 /* CSI ISR for transactional APIs. */
109 #if defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050)
110 static csi_isr_t s_csiIsr = (csi_isr_t)DefaultISR;
111 #else
112 static csi_isr_t s_csiIsr;
113 #endif
114
115 /*******************************************************************************
116 * Code
117 ******************************************************************************/
CSI_GetInstance(CSI_Type * base)118 static uint32_t CSI_GetInstance(CSI_Type *base)
119 {
120 uint32_t instance;
121
122 /* Find the instance index from base address mappings. */
123 for (instance = 0; instance < ARRAY_SIZE(s_csiBases); instance++)
124 {
125 if (s_csiBases[instance] == base)
126 {
127 break;
128 }
129 }
130
131 assert(instance < ARRAY_SIZE(s_csiBases));
132
133 return instance;
134 }
135
136 #if !CSI_DRIVER_FRAG_MODE
CSI_InitBufferQueue(buf_queue_t * bq)137 static void CSI_InitBufferQueue(buf_queue_t *bq)
138 {
139 bq->head = -1;
140 bq->tail = -1;
141 }
142
CSI_IsBufferQueueEmpty(buf_queue_t * bq)143 static bool CSI_IsBufferQueueEmpty(buf_queue_t *bq)
144 {
145 return bq->head == -1;
146 }
147
CSI_IsBufferQueueFull(buf_queue_t * bq)148 static bool CSI_IsBufferQueueFull(buf_queue_t *bq)
149 {
150 return bq->tail == (CSI_DRIVER_QUEUE_SIZE - 1);
151 }
152
CSI_BufferQueueCount(buf_queue_t * bq)153 static uint32_t CSI_BufferQueueCount(buf_queue_t *bq)
154 {
155 if (CSI_IsBufferQueueEmpty(bq))
156 {
157 return 0;
158 }
159 return bq->tail - bq->head + 1;
160 }
161
CSI_EnqueueBuffer(buf_queue_t * bq,uint32_t addr)162 static status_t CSI_EnqueueBuffer(buf_queue_t *bq, uint32_t addr)
163 {
164 if (CSI_IsBufferQueueFull(bq))
165 {
166 return kStatus_CSI_QueueFull;
167 }
168
169 if (CSI_IsBufferQueueEmpty(bq))
170 {
171 bq->head = 0;
172 }
173
174 bq->tail++;
175 bq->addr[bq->tail] = addr;
176
177 return kStatus_Success;
178 }
179
CSI_DequeueBuffer(buf_queue_t * bq)180 static uint32_t CSI_DequeueBuffer(buf_queue_t *bq)
181 {
182 if (CSI_IsBufferQueueEmpty(bq))
183 {
184 return 0;
185 }
186
187 uint32_t addr = bq->addr[bq->head];
188
189 if (bq->head == bq->tail)
190 {
191 bq->head = -1;
192 bq->tail = -1;
193 }
194 else
195 {
196 bq->head++;
197 }
198
199 return addr;
200 }
201
CSI_GetRxBufferAddr(CSI_Type * base,uint8_t index)202 static uint32_t CSI_GetRxBufferAddr(CSI_Type *base, uint8_t index)
203 {
204 uint32_t addr;
205
206 if (index != 0U)
207 {
208 addr = CSI_REG_DMASA_FB2(base);
209 }
210 else
211 {
212 addr = CSI_REG_DMASA_FB1(base);
213 }
214
215 return CSI_ADDR_IP_2_CPU(addr);
216 }
217
218 #endif /* CSI_DRIVER_FRAG_MODE */
219
220 /*!
221 * brief Initialize the CSI.
222 *
223 * This function enables the CSI peripheral clock, and resets the CSI registers.
224 *
225 * param base CSI peripheral base address.
226 * param config Pointer to the configuration structure.
227 *
228 * retval kStatus_Success Initialize successfully.
229 * retval kStatus_InvalidArgument Initialize failed because of invalid argument.
230 */
CSI_Init(CSI_Type * base,const csi_config_t * config)231 status_t CSI_Init(CSI_Type *base, const csi_config_t *config)
232 {
233 assert(NULL != config);
234 uint32_t reg;
235 uint32_t imgWidth_Bytes;
236 uint8_t busCyclePerPixel;
237
238 imgWidth_Bytes = (uint32_t)config->width * (uint32_t)config->bytesPerPixel;
239
240 /* The image width and frame buffer pitch should be multiple of 8-bytes. */
241 if ((0U != (imgWidth_Bytes & 0x07U)) || (0U != ((uint32_t)config->linePitch_Bytes & 0x07U)))
242 {
243 return kStatus_InvalidArgument;
244 }
245
246 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
247 uint32_t instance = CSI_GetInstance(base);
248 CLOCK_EnableClock(s_csiClocks[instance]);
249 #endif
250
251 CSI_Reset(base);
252
253 /* Configure CSICR1. CSICR1 has been reset to the default value, so could write it directly. */
254 reg = ((uint32_t)config->workMode) | config->polarityFlags | CSI_CR1_FCC_MASK;
255
256 if (config->useExtVsync)
257 {
258 reg |= CSI_CR1_EXT_VSYNC_MASK;
259 }
260
261 CSI_REG_CR1(base) = reg;
262
263 /*
264 * Generally, CSIIMAG_PARA[IMAGE_WIDTH] indicates how many data bus cycles per line.
265 * One special case is when receiving 24-bit pixels through 8-bit data bus.
266 * In this case, the CSIIMAG_PARA[IMAGE_WIDTH] should be set to the pixel number per line.
267 */
268 if ((kCSI_DataBus8Bit == config->dataBus) && (2U == config->bytesPerPixel))
269 {
270 busCyclePerPixel = 2U;
271 }
272 else
273 {
274 busCyclePerPixel = 1U;
275 }
276
277 if (4U == config->bytesPerPixel)
278 {
279 CSI_REG_CR18(base) |= CSI_CR18_PARALLEL24_EN_MASK;
280 }
281
282 if (kCSI_DataBus16Bit == config->dataBus)
283 {
284 CSI_REG_CR3(base) |= CSI_CR3_TWO_8BIT_SENSOR_MASK;
285 }
286
287 /* Image parameter. */
288 CSI_REG_IMAG_PARA(base) =
289 (((uint32_t)config->width * (uint32_t)busCyclePerPixel) << CSI_IMAG_PARA_IMAGE_WIDTH_SHIFT) |
290 ((uint32_t)(config->height) << CSI_IMAG_PARA_IMAGE_HEIGHT_SHIFT);
291
292 /* The CSI frame buffer bus is 8-byte width. */
293 CSI_REG_FBUF_PARA(base) = (uint32_t)((config->linePitch_Bytes - imgWidth_Bytes) / 8U)
294 << CSI_FBUF_PARA_FBUF_STRIDE_SHIFT;
295
296 /* Enable auto ECC. */
297 CSI_REG_CR3(base) |= CSI_CR3_ECC_AUTO_EN_MASK;
298
299 /*
300 * For better performance.
301 * The DMA burst size could be set to 16 * 8 byte, 8 * 8 byte, or 4 * 8 byte,
302 * choose the best burst size based on bytes per line.
303 */
304 if (0U == (imgWidth_Bytes % (8U * 16U)))
305 {
306 CSI_REG_CR2(base) = CSI_CR2_DMA_BURST_TYPE_RFF(3U);
307 CSI_REG_CR3(base) = (CSI_REG_CR3(base) & ~CSI_CR3_RxFF_LEVEL_MASK) | ((2U << CSI_CR3_RxFF_LEVEL_SHIFT));
308 }
309 else if (0U == (imgWidth_Bytes % (8U * 8U)))
310 {
311 CSI_REG_CR2(base) = CSI_CR2_DMA_BURST_TYPE_RFF(2U);
312 CSI_REG_CR3(base) = (CSI_REG_CR3(base) & ~CSI_CR3_RxFF_LEVEL_MASK) | ((1U << CSI_CR3_RxFF_LEVEL_SHIFT));
313 }
314 else
315 {
316 CSI_REG_CR2(base) = CSI_CR2_DMA_BURST_TYPE_RFF(1U);
317 CSI_REG_CR3(base) = (CSI_REG_CR3(base) & ~CSI_CR3_RxFF_LEVEL_MASK) | ((0U << CSI_CR3_RxFF_LEVEL_SHIFT));
318 }
319
320 CSI_ReflashFifoDma(base, kCSI_RxFifo);
321
322 return kStatus_Success;
323 }
324
325 /*!
326 * brief De-initialize the CSI.
327 *
328 * This function disables the CSI peripheral clock.
329 *
330 * param base CSI peripheral base address.
331 */
CSI_Deinit(CSI_Type * base)332 void CSI_Deinit(CSI_Type *base)
333 {
334 /* Disable transfer first. */
335 CSI_Stop(base);
336 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
337 uint32_t instance = CSI_GetInstance(base);
338 CLOCK_DisableClock(s_csiClocks[instance]);
339 #endif
340 }
341
342 /*!
343 * brief Reset the CSI.
344 *
345 * This function resets the CSI peripheral registers to default status.
346 *
347 * param base CSI peripheral base address.
348 */
CSI_Reset(CSI_Type * base)349 void CSI_Reset(CSI_Type *base)
350 {
351 uint32_t csisr;
352
353 /* Disable transfer first. */
354 CSI_Stop(base);
355
356 /* Disable DMA request. */
357 CSI_REG_CR3(base) = 0U;
358
359 /* Reset the fame count. */
360 CSI_REG_CR3(base) |= CSI_CR3_FRMCNT_RST_MASK;
361 while (0U != (CSI_REG_CR3(base) & CSI_CR3_FRMCNT_RST_MASK))
362 {
363 }
364
365 /* Clear the RX FIFO. */
366 CSI_ClearFifo(base, kCSI_AllFifo);
367
368 /* Reflash DMA. */
369 CSI_ReflashFifoDma(base, kCSI_AllFifo);
370
371 /* Clear the status. */
372 csisr = CSI_REG_SR(base);
373 CSI_REG_SR(base) = csisr;
374
375 /* Set the control registers to default value. */
376 CSI_REG_CR1(base) = CSI_CR1_HSYNC_POL_MASK | CSI_CR1_EXT_VSYNC_MASK;
377 CSI_REG_CR2(base) = 0U;
378 CSI_REG_CR3(base) = 0U;
379 #if defined(CSI_CR18_CSI_LCDIF_BUFFER_LINES)
380 CSI_REG_CR18(base) = CSI_CR18_AHB_HPROT(0x0DU) | CSI_CR18_CSI_LCDIF_BUFFER_LINES(0x02U);
381 #else
382 CSI_REG_CR18(base) = CSI_CR18_AHB_HPROT(0x0DU);
383 #endif
384 CSI_REG_FBUF_PARA(base) = 0U;
385 CSI_REG_IMAG_PARA(base) = 0U;
386 }
387
388 /*!
389 * brief Get the default configuration for to initialize the CSI.
390 *
391 * The default configuration value is:
392 *
393 * code
394 config->width = 320U;
395 config->height = 240U;
396 config->polarityFlags = kCSI_HsyncActiveHigh | kCSI_DataLatchOnRisingEdge;
397 config->bytesPerPixel = 2U;
398 config->linePitch_Bytes = 320U * 2U;
399 config->workMode = kCSI_GatedClockMode;
400 config->dataBus = kCSI_DataBus8Bit;
401 config->useExtVsync = true;
402 endcode
403 *
404 * param config Pointer to the CSI configuration.
405 */
CSI_GetDefaultConfig(csi_config_t * config)406 void CSI_GetDefaultConfig(csi_config_t *config)
407 {
408 assert(NULL != config);
409
410 /* Initializes the configure structure to zero. */
411 (void)memset(config, 0, sizeof(*config));
412
413 config->width = 320U;
414 config->height = 240U;
415 config->polarityFlags = (uint32_t)kCSI_HsyncActiveHigh | (uint32_t)kCSI_DataLatchOnRisingEdge;
416 config->bytesPerPixel = 2U;
417 config->linePitch_Bytes = 320U * 2U;
418 config->workMode = kCSI_GatedClockMode;
419 config->dataBus = kCSI_DataBus8Bit;
420 config->useExtVsync = true;
421 }
422
423 /*!
424 * brief Set the RX frame buffer address.
425 *
426 * param base CSI peripheral base address.
427 * param index Buffer index.
428 * param addr Frame buffer address to set.
429 */
CSI_SetRxBufferAddr(CSI_Type * base,uint8_t index,uint32_t addr)430 void CSI_SetRxBufferAddr(CSI_Type *base, uint8_t index, uint32_t addr)
431 {
432 addr = CSI_ADDR_CPU_2_IP(addr);
433
434 if (0U != index)
435 {
436 CSI_REG_DMASA_FB2(base) = addr;
437 }
438 else
439 {
440 CSI_REG_DMASA_FB1(base) = addr;
441 }
442 }
443
444 /*!
445 * brief Clear the CSI FIFO.
446 *
447 * This function clears the CSI FIFO.
448 *
449 * param base CSI peripheral base address.
450 * param fifo The FIFO to clear.
451 */
CSI_ClearFifo(CSI_Type * base,csi_fifo_t fifo)452 void CSI_ClearFifo(CSI_Type *base, csi_fifo_t fifo)
453 {
454 uint32_t cr1;
455 uint32_t mask = 0U;
456
457 /* The FIFO could only be cleared when CSICR1[FCC] = 0, so first clear the FCC. */
458 cr1 = CSI_REG_CR1(base);
459 CSI_REG_CR1(base) = (cr1 & ~CSI_CR1_FCC_MASK);
460
461 if (0U != ((uint32_t)fifo & (uint32_t)kCSI_RxFifo))
462 {
463 mask |= CSI_CR1_CLR_RXFIFO_MASK;
464 }
465
466 if (0U != ((uint32_t)fifo & (uint32_t)kCSI_StatFifo))
467 {
468 mask |= CSI_CR1_CLR_STATFIFO_MASK;
469 }
470
471 CSI_REG_CR1(base) = (cr1 & ~CSI_CR1_FCC_MASK) | mask;
472
473 /* Wait clear completed. */
474 while (0U != (CSI_REG_CR1(base) & mask))
475 {
476 }
477
478 /* Recover the FCC. */
479 CSI_REG_CR1(base) = cr1;
480 }
481
482 /*!
483 * brief Reflash the CSI FIFO DMA.
484 *
485 * This function reflashes the CSI FIFO DMA.
486 *
487 * For RXFIFO, there are two frame buffers. When the CSI module started, it saves
488 * the frames to frame buffer 0 then frame buffer 1, the two buffers will be
489 * written by turns. After reflash DMA using this function, the CSI is reset to
490 * save frame to buffer 0.
491 *
492 * param base CSI peripheral base address.
493 * param fifo The FIFO DMA to reflash.
494 */
CSI_ReflashFifoDma(CSI_Type * base,csi_fifo_t fifo)495 void CSI_ReflashFifoDma(CSI_Type *base, csi_fifo_t fifo)
496 {
497 uint32_t cr3 = 0U;
498
499 if (0U != ((uint32_t)fifo & (uint32_t)kCSI_RxFifo))
500 {
501 cr3 |= CSI_CR3_DMA_REFLASH_RFF_MASK;
502 }
503
504 if (0U != ((uint32_t)fifo & (uint32_t)kCSI_StatFifo))
505 {
506 cr3 |= CSI_CR3_DMA_REFLASH_SFF_MASK;
507 }
508
509 CSI_REG_CR3(base) |= cr3;
510
511 /* Wait clear completed. */
512 while (0U != (CSI_REG_CR3(base) & cr3))
513 {
514 }
515 }
516
517 /*!
518 * brief Enable or disable the CSI FIFO DMA request.
519 *
520 * param base CSI peripheral base address.
521 * param fifo The FIFO DMA reques to enable or disable.
522 * param enable True to enable, false to disable.
523 */
CSI_EnableFifoDmaRequest(CSI_Type * base,csi_fifo_t fifo,bool enable)524 void CSI_EnableFifoDmaRequest(CSI_Type *base, csi_fifo_t fifo, bool enable)
525 {
526 uint32_t cr3 = 0U;
527
528 if (0U != ((uint32_t)fifo & (uint32_t)kCSI_RxFifo))
529 {
530 cr3 |= CSI_CR3_DMA_REQ_EN_RFF_MASK;
531 }
532
533 if (0U != ((uint32_t)fifo & (uint32_t)kCSI_StatFifo))
534 {
535 cr3 |= CSI_CR3_DMA_REQ_EN_SFF_MASK;
536 }
537
538 if (enable)
539 {
540 CSI_REG_CR3(base) |= cr3;
541 }
542 else
543 {
544 CSI_REG_CR3(base) &= ~cr3;
545 }
546 }
547
548 /*!
549 * brief Enables CSI interrupt requests.
550 *
551 * param base CSI peripheral base address.
552 * param mask The interrupts to enable, pass in as OR'ed value of ref _csi_interrupt_enable.
553 */
CSI_EnableInterrupts(CSI_Type * base,uint32_t mask)554 void CSI_EnableInterrupts(CSI_Type *base, uint32_t mask)
555 {
556 CSI_REG_CR1(base) |= (mask & CSI_CR1_INT_EN_MASK);
557 CSI_REG_CR3(base) |= (mask & CSI_CR3_INT_EN_MASK);
558 CSI_REG_CR18(base) |= ((mask & CSI_CR18_INT_EN_MASK) >> 6U);
559 }
560
561 /*!
562 * brief Disable CSI interrupt requests.
563 *
564 * param base CSI peripheral base address.
565 * param mask The interrupts to disable, pass in as OR'ed value of ref _csi_interrupt_enable.
566 */
CSI_DisableInterrupts(CSI_Type * base,uint32_t mask)567 void CSI_DisableInterrupts(CSI_Type *base, uint32_t mask)
568 {
569 CSI_REG_CR1(base) &= ~(mask & CSI_CR1_INT_EN_MASK);
570 CSI_REG_CR3(base) &= ~(mask & CSI_CR3_INT_EN_MASK);
571 CSI_REG_CR18(base) &= ~((mask & CSI_CR18_INT_EN_MASK) >> 6U);
572 }
573
574 #if !CSI_DRIVER_FRAG_MODE
575 /*!
576 * brief Initializes the CSI handle.
577 *
578 * This function initializes CSI handle, it should be called before any other
579 * CSI transactional functions.
580 *
581 * param base CSI peripheral base address.
582 * param handle Pointer to the handle structure.
583 * param callback Callback function for CSI transfer.
584 * param userData Callback function parameter.
585 *
586 * retval kStatus_Success Handle created successfully.
587 */
CSI_TransferCreateHandle(CSI_Type * base,csi_handle_t * handle,csi_transfer_callback_t callback,void * userData)588 status_t CSI_TransferCreateHandle(CSI_Type *base,
589 csi_handle_t *handle,
590 csi_transfer_callback_t callback,
591 void *userData)
592 {
593 assert(NULL != handle);
594 uint32_t instance;
595
596 (void)memset(handle, 0, sizeof(*handle));
597
598 /* Set the callback and user data. */
599 handle->callback = callback;
600 handle->userData = userData;
601
602 /* Initialize buffer queues. */
603 CSI_InitBufferQueue(&handle->emptyBufferQueue);
604 CSI_InitBufferQueue(&handle->fullBufferQueue);
605
606 /* Get instance from peripheral base address. */
607 instance = CSI_GetInstance(base);
608
609 /* Save the handle in global variables to support the double weak mechanism. */
610 s_csiHandle[instance] = handle;
611
612 s_csiIsr = CSI_TransferHandleIRQ;
613
614 /* Enable interrupt. */
615 (void)EnableIRQ(s_csiIRQ[instance]);
616
617 return kStatus_Success;
618 }
619
620 /*!
621 * brief Start the transfer using transactional functions.
622 *
623 * When the empty frame buffers have been submit to CSI driver using function
624 * ref CSI_TransferSubmitEmptyBuffer, user could call this function to start
625 * the transfer. The incoming frame will be saved to the empty frame buffer,
626 * and user could be optionally notified through callback function.
627 *
628 * param base CSI peripheral base address.
629 * param handle Pointer to the handle structure.
630 *
631 * retval kStatus_Success Started successfully.
632 * retval kStatus_CSI_NoEmptyBuffer Could not start because no empty frame buffer in queue.
633 */
CSI_TransferStart(CSI_Type * base,csi_handle_t * handle)634 status_t CSI_TransferStart(CSI_Type *base, csi_handle_t *handle)
635 {
636 assert(NULL != handle);
637
638 if (CSI_BufferQueueCount(&handle->emptyBufferQueue) < CSI_MAX_ACTIVE_FRAME_NUM)
639 {
640 return kStatus_CSI_NoEmptyBuffer;
641 }
642
643 /*
644 * Write to memory from first completed frame.
645 * DMA base addr switch at the edge of the first data of each frame, thus
646 * if one frame is broken, it could be reset at the next frame.
647 */
648 CSI_REG_CR18(base) = (CSI_REG_CR18(base) & ~CSI_CR18_MASK_OPTION_MASK) | CSI_CR18_MASK_OPTION(0) |
649 CSI_CR18_BASEADDR_SWITCH_SEL_MASK | CSI_CR18_BASEADDR_SWITCH_EN_MASK;
650
651 /* Load the frame buffer to CSI register, there are at least two empty buffers. */
652 CSI_REG_DMASA_FB1(base) = CSI_ADDR_CPU_2_IP(CSI_DequeueBuffer(&handle->emptyBufferQueue));
653 CSI_REG_DMASA_FB2(base) = CSI_ADDR_CPU_2_IP(CSI_DequeueBuffer(&handle->emptyBufferQueue));
654
655 handle->activeBufferNum = CSI_MAX_ACTIVE_FRAME_NUM;
656
657 /* After reflash DMA, the CSI saves frame to frame buffer 0. */
658 CSI_ReflashFifoDma(base, kCSI_RxFifo);
659
660 handle->transferStarted = true;
661
662 CSI_EnableInterrupts(
663 base, (uint32_t)kCSI_RxBuffer1DmaDoneInterruptEnable | (uint32_t)kCSI_RxBuffer0DmaDoneInterruptEnable);
664
665 CSI_Start(base);
666
667 return kStatus_Success;
668 }
669
670 /*!
671 * brief Stop the transfer using transactional functions.
672 *
673 * The driver does not clean the full frame buffers in queue. In other words, after
674 * calling this function, user still could get the full frame buffers in queue
675 * using function ref CSI_TransferGetFullBuffer.
676 *
677 * param base CSI peripheral base address.
678 * param handle Pointer to the handle structure.
679 *
680 * retval kStatus_Success Stoped successfully.
681 */
CSI_TransferStop(CSI_Type * base,csi_handle_t * handle)682 status_t CSI_TransferStop(CSI_Type *base, csi_handle_t *handle)
683 {
684 assert(NULL != handle);
685 uint8_t activeBufferNum;
686 uint8_t bufIdx;
687
688 CSI_Stop(base);
689 CSI_DisableInterrupts(
690 base, (uint32_t)kCSI_RxBuffer1DmaDoneInterruptEnable | (uint32_t)kCSI_RxBuffer0DmaDoneInterruptEnable);
691
692 activeBufferNum = handle->activeBufferNum;
693
694 handle->transferStarted = false;
695 handle->activeBufferNum = 0;
696
697 /*
698 * Put active buffers to empty queue.
699 *
700 * If there is only one active frame buffers, then FB0 and FB1 use the same address,
701 * put FB0 to empty buffer queue is OK.
702 */
703 for (bufIdx = 0; bufIdx < activeBufferNum; bufIdx++)
704 {
705 CSI_EnqueueBuffer(&handle->emptyBufferQueue, CSI_GetRxBufferAddr(base, bufIdx));
706 }
707
708 return kStatus_Success;
709 }
710
711 /*!
712 * brief Submit empty frame buffer to queue.
713 *
714 * This function could be called before ref CSI_TransferStart or after ref
715 * CSI_TransferStart. If there is no room in queue to store the empty frame
716 * buffer, this function returns error.
717 *
718 * param base CSI peripheral base address.
719 * param handle Pointer to the handle structure.
720 * param frameBuffer Empty frame buffer to submit.
721 *
722 * retval kStatus_Success Submitted successfully.
723 * retval kStatus_CSI_QueueFull Could not submit because there is no room in queue.
724 */
CSI_TransferSubmitEmptyBuffer(CSI_Type * base,csi_handle_t * handle,uint32_t frameBuffer)725 status_t CSI_TransferSubmitEmptyBuffer(CSI_Type *base, csi_handle_t *handle, uint32_t frameBuffer)
726 {
727 uint32_t csicr1;
728 status_t status = kStatus_Success;
729
730 /* Disable the interrupt to protect the index information in handle. */
731 csicr1 = CSI_REG_CR1(base);
732
733 CSI_REG_CR1(base) = (csicr1 & ~(CSI_CR1_FB2_DMA_DONE_INTEN_MASK | CSI_CR1_FB1_DMA_DONE_INTEN_MASK));
734
735 if (handle->activeBufferNum == 1U)
736 {
737 /* Only 1 active framebuffer left, set the newly submitted buffer as active framebuffer */
738 CSI_SetRxBufferAddr(base, handle->dmaDoneBufferIdx, frameBuffer);
739 handle->activeBufferNum++;
740 }
741 else
742 {
743 status = CSI_EnqueueBuffer(&handle->emptyBufferQueue, frameBuffer);
744 }
745
746 CSI_REG_CR1(base) = csicr1;
747
748 return status;
749 }
750
751 /*!
752 * brief Get one full frame buffer from queue.
753 *
754 * After the transfer started using function ref CSI_TransferStart, the incoming
755 * frames will be saved to the empty frame buffers in queue. This function gets
756 * the full-filled frame buffer from the queue. If there is no full frame buffer
757 * in queue, this function returns error.
758 *
759 * param base CSI peripheral base address.
760 * param handle Pointer to the handle structure.
761 * param frameBuffer Full frame buffer.
762 *
763 * retval kStatus_Success Got full framebuffer successfully.
764 * retval kStatus_CSI_NoFullBuffer There is no full frame buffer in queue.
765 */
CSI_TransferGetFullBuffer(CSI_Type * base,csi_handle_t * handle,uint32_t * frameBuffer)766 status_t CSI_TransferGetFullBuffer(CSI_Type *base, csi_handle_t *handle, uint32_t *frameBuffer)
767 {
768 uint32_t csicr1;
769 status_t status = kStatus_Success;
770
771 /* Disable the interrupt to protect the index information in handle. */
772 csicr1 = CSI_REG_CR1(base);
773
774 CSI_REG_CR1(base) = (csicr1 & ~(CSI_CR1_FB2_DMA_DONE_INTEN_MASK | CSI_CR1_FB1_DMA_DONE_INTEN_MASK));
775
776 if (CSI_IsBufferQueueEmpty(&handle->fullBufferQueue))
777 {
778 frameBuffer = NULL;
779 status = kStatus_CSI_NoFullBuffer;
780 }
781 else
782 {
783 *frameBuffer = CSI_DequeueBuffer(&handle->fullBufferQueue);
784 }
785
786 CSI_REG_CR1(base) = csicr1;
787
788 return status;
789 }
790
791 /*!
792 * brief CSI IRQ handle function.
793 *
794 * This function handles the CSI IRQ request to work with CSI driver transactional
795 * APIs.
796 *
797 * param base CSI peripheral base address.
798 * param handle CSI handle pointer.
799 */
CSI_TransferHandleIRQ(CSI_Type * base,csi_handle_t * handle)800 void CSI_TransferHandleIRQ(CSI_Type *base, csi_handle_t *handle)
801 {
802 uint32_t csisr = CSI_REG_SR(base);
803 uint32_t full_fb;
804 uint32_t fb;
805 bool isFullBufferEnqueued = true;
806
807 /* Clear the error flags. */
808 CSI_REG_SR(base) = csisr;
809
810 if ((csisr & (CSI_SR_DMA_TSF_DONE_FB2_MASK | CSI_SR_DMA_TSF_DONE_FB1_MASK)) ==
811 (CSI_SR_DMA_TSF_DONE_FB2_MASK | CSI_SR_DMA_TSF_DONE_FB1_MASK))
812 {
813 /* Both FB1 and FB2 flags assert, we don't know which framebuffer is done, so skip them. */
814 ;
815 }
816 else if (0U != (csisr & (CSI_SR_DMA_TSF_DONE_FB2_MASK | CSI_SR_DMA_TSF_DONE_FB1_MASK)))
817 {
818 if (0U != (csisr & CSI_SR_DMA_TSF_DONE_FB2_MASK))
819 {
820 handle->dmaDoneBufferIdx = 1;
821 }
822 else
823 {
824 handle->dmaDoneBufferIdx = 0;
825 }
826
827 full_fb = CSI_GetRxBufferAddr(base, handle->dmaDoneBufferIdx);
828
829 if (CSI_BufferQueueCount(&handle->emptyBufferQueue) > 0U)
830 {
831 if (CSI_IsBufferQueueFull(&handle->fullBufferQueue))
832 {
833 /* Drop the oldest buffer */
834 CSI_DequeueBuffer(&handle->fullBufferQueue);
835 }
836 CSI_EnqueueBuffer(&handle->fullBufferQueue, full_fb);
837
838 /*
839 * Get an empty buffer from the queue to use as an active
840 * framebuffer to replace the full-filled framebuffer
841 */
842 fb = CSI_DequeueBuffer(&handle->emptyBufferQueue);
843 CSI_SetRxBufferAddr(base, handle->dmaDoneBufferIdx, fb);
844 }
845 else if (handle->activeBufferNum > 1U)
846 {
847 if (CSI_IsBufferQueueFull(&handle->fullBufferQueue))
848 {
849 /* Drop the oldest buffer */
850 CSI_DequeueBuffer(&handle->fullBufferQueue);
851 }
852 CSI_EnqueueBuffer(&handle->fullBufferQueue, full_fb);
853
854 handle->activeBufferNum--;
855
856 /*
857 * After putting the full-filled framebuffer to the queue, only one
858 * active framebuffer left, both FB1 and FB2 need to be set to this
859 * active buffer address.
860 */
861 fb = CSI_GetRxBufferAddr(base, handle->dmaDoneBufferIdx ^ 1U);
862 CSI_SetRxBufferAddr(base, handle->dmaDoneBufferIdx, fb);
863 }
864 else if (handle->activeBufferNum == 1U)
865 {
866 isFullBufferEnqueued = false;
867 }
868 else
869 {
870 }
871
872 if (NULL != handle->callback && isFullBufferEnqueued)
873 {
874 handle->callback(base, handle, kStatus_CSI_FrameDone, handle->userData);
875 }
876 }
877 else
878 {
879 }
880 }
881
882 #else /* CSI_DRIVER_FRAG_MODE */
883
884 #if defined(__CC_ARM)
CSI_ExtractYFromYUYV(void * datBase,const void * dmaBase,size_t count)885 __asm void CSI_ExtractYFromYUYV(void *datBase, const void *dmaBase, size_t count)
886 {
887 /* clang-format off */
888 push {r4-r7, lr}
889 10
890 LDMIA R1!, {r3-r6}
891 bfi r7, r3, #0, #8 /* Y0 */
892 bfi ip, r5, #0, #8 /* Y4 */
893 lsr r3, r3, #16
894 lsr r5, r5, #16
895 bfi r7, r3, #8, #8 /* Y1 */
896 bfi ip, r5, #8, #8 /* Y5 */
897 bfi r7, r4, #16, #8 /* Y2 */
898 bfi ip, r6, #16, #8 /* Y6 */
899 lsr r4, r4, #16
900 lsr r6, r6, #16
901 bfi r7, r4, #24, #8 /* Y3 */
902 bfi ip, r6, #24, #8 /* Y7 */
903 STMIA r0!, {r7, ip}
904 subs r2, #8
905 bne %b10
906 pop {r4-r7, pc}
907 /* clang-format on */
908 }
909
CSI_ExtractYFromUYVY(void * datBase,const void * dmaBase,size_t count)910 __asm void CSI_ExtractYFromUYVY(void *datBase, const void *dmaBase, size_t count)
911 {
912 /* clang-format off */
913 push {r4-r7, lr}
914 10
915 LDMIA R1!, {r3-r6}
916 lsr r3, r3, #8
917 lsr r5, r5, #8
918 bfi r7, r3, #0, #8 /* Y0 */
919 bfi ip, r5, #0, #8 /* Y4 */
920 lsr r3, r3, #16
921 lsr r5, r5, #16
922 bfi r7, r3, #8, #8 /* Y1 */
923 bfi ip, r5, #8, #8 /* Y5 */
924 lsr r4, r4, #8
925 lsr r6, r6, #8
926 bfi r7, r4, #16, #8 /* Y2 */
927 bfi ip, r6, #16, #8 /* Y6 */
928 lsr r4, r4, #16
929 lsr r6, r6, #16
930 bfi r7, r4, #24, #8 /* Y3 */
931 bfi ip, r6, #24, #8 /* Y7 */
932 STMIA r0!, {r7, ip}
933 subs r2, #8
934 bne %b10
935 pop {r4-r7, pc}
936 /* clang-format on */
937 }
938
939 #elif (defined(__GNUC__) || defined(__ICCARM__)) || defined(__ARMCC_VERSION)
940 #if defined(__ICCARM__)
941 #pragma diag_suppress = Pe940
942 #endif
943 __attribute__((naked)) void CSI_ExtractYFromYUYV(void *datBase, const void *dmaBase, size_t count);
CSI_ExtractYFromYUYV(void * datBase,const void * dmaBase,size_t count)944 void CSI_ExtractYFromYUYV(void *datBase, const void *dmaBase, size_t count)
945 {
946 /* clang-format off */
947 __asm volatile(
948 " push {r1-r7, r12, lr} \n"
949 "loop0: \n"
950 " ldmia r1!, {r3-r6} \n"
951 " bfi r7, r3, #0, #8 \n" /* Y0 */
952 " bfi r12, r5, #0, #8 \n" /* Y4 */
953 " lsr r3, r3, #16 \n"
954 " lsr r5, r5, #16 \n"
955 " bfi r7, r3, #8, #8 \n" /* Y1 */
956 " bfi r12, r5, #8, #8 \n" /* Y5 */
957 " bfi r7, r4, #16, #8 \n" /* Y2 */
958 " bfi r12, r6, #16, #8 \n" /* Y6 */
959 " lsr r4, r4, #16 \n"
960 " lsr r6, r6, #16 \n"
961 " bfi r7, r4, #24, #8 \n" /* Y3 */
962 " bfi r12, r6, #24, #8 \n" /* Y7 */
963 " stmia r0!, {r7, r12} \n"
964 " subs r2, #8 \n"
965 " bne loop0 \n"
966 " pop {r1-r7, r12, pc} \n");
967 /* clang-format on */
968 }
969
970 __attribute__((naked)) void CSI_ExtractYFromUYVY(void *datBase, const void *dmaBase, size_t count);
CSI_ExtractYFromUYVY(void * datBase,const void * dmaBase,size_t count)971 void CSI_ExtractYFromUYVY(void *datBase, const void *dmaBase, size_t count)
972 {
973 /* clang-format off */
974 __asm volatile(
975 " push {r1-r7, r12, lr} \n"
976 "loop1: \n"
977 " ldmia r1!, {r3-r6} \n"
978 " lsr r3, r3, #8 \n"
979 " lsr r5, r5, #8 \n"
980 " bfi r7, r3, #0, #8 \n" /* Y0 */
981 " bfi r12, r5, #0, #8 \n" /* Y4 */
982 " lsr r3, r3, #16 \n"
983 " lsr r5, r5, #16 \n"
984 " bfi r7, r3, #8, #8 \n" /* Y1 */
985 " bfi r12, r5, #8, #8 \n" /* Y5 */
986 " lsr r4, r4, #8 \n"
987 " lsr r6, r6, #8 \n"
988 " bfi r7, r4, #16, #8 \n" /* Y2 */
989 " bfi r12, r6, #16, #8 \n" /* Y6 */
990 " lsr r4, r4, #16 \n"
991 " lsr r6, r6, #16 \n"
992 " bfi r7, r4, #24, #8 \n" /* Y3 */
993 " bfi r12, r6, #24, #8 \n" /* Y7 */
994 " stmia r0!, {r7, r12} \n"
995 " subs r2, #8 \n"
996 " bne loop1 \n"
997 " pop {r1-r7, r12, pc} \n");
998 /* clang-format on */
999 }
1000 #if defined(__ICCARM__)
1001 #pragma diag_default = Pe940
1002 #endif
1003 #else
1004 #error Toolchain not supported.
1005 #endif
1006
CSI_MemCopy(void * pDest,const void * pSrc,size_t cnt)1007 static void CSI_MemCopy(void *pDest, const void *pSrc, size_t cnt)
1008 {
1009 (void)memcpy(pDest, pSrc, cnt);
1010 }
1011
1012 /*!
1013 * brief Initialize the CSI to work in fragment mode.
1014 *
1015 * This function enables the CSI peripheral clock, and resets the CSI registers.
1016 *
1017 * param base CSI peripheral base address.
1018 */
CSI_FragModeInit(CSI_Type * base)1019 void CSI_FragModeInit(CSI_Type *base)
1020 {
1021 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
1022 uint32_t instance = CSI_GetInstance(base);
1023 CLOCK_EnableClock(s_csiClocks[instance]);
1024 #endif
1025
1026 CSI_Reset(base);
1027 }
1028
1029 /*!
1030 * brief De-initialize the CSI.
1031 *
1032 * This function disables the CSI peripheral clock.
1033 *
1034 * param base CSI peripheral base address.
1035 */
CSI_FragModeDeinit(CSI_Type * base)1036 void CSI_FragModeDeinit(CSI_Type *base)
1037 {
1038 CSI_Deinit(base);
1039 }
1040
1041 /*!
1042 * brief Create handle for CSI work in fragment mode.
1043 *
1044 * param base CSI peripheral base address.
1045 * param handle Pointer to the transactional handle.
1046 * param config Pointer to the configuration structure.
1047 * param callback Callback function for CSI transfer.
1048 * param userData Callback function parameter.
1049 *
1050 * retval kStatus_Success Initialize successfully.
1051 * retval kStatus_InvalidArgument Initialize failed because of invalid argument.
1052 */
CSI_FragModeCreateHandle(CSI_Type * base,csi_frag_handle_t * handle,const csi_frag_config_t * config,csi_frag_transfer_callback_t callback,void * userData)1053 status_t CSI_FragModeCreateHandle(CSI_Type *base,
1054 csi_frag_handle_t *handle,
1055 const csi_frag_config_t *config,
1056 csi_frag_transfer_callback_t callback,
1057 void *userData)
1058 {
1059 assert(NULL != config);
1060 uint32_t reg;
1061 uint32_t instance;
1062 uint32_t imgWidth_Bytes;
1063
1064 if (config->dataBus != kCSI_DataBus8Bit)
1065 {
1066 return kStatus_InvalidArgument;
1067 }
1068
1069 imgWidth_Bytes = (uint32_t)config->width * CSI_FRAG_INPUT_BYTES_PER_PIXEL;
1070
1071 /* The image buffer line width should be multiple of 8-bytes. */
1072 if ((imgWidth_Bytes & 0x07U) != 0U)
1073 {
1074 return kStatus_InvalidArgument;
1075 }
1076
1077 /* Camera frame height must be dividable by DMA buffer line. */
1078 if (config->height % config->dmaBufferLine != 0U)
1079 {
1080 return kStatus_InvalidArgument;
1081 }
1082
1083 (void)memset(handle, 0, sizeof(*handle));
1084 handle->callback = callback;
1085 handle->userData = userData;
1086 handle->height = config->height;
1087 handle->width = config->width;
1088 handle->maxLinePerFrag = config->dmaBufferLine;
1089 handle->dmaBytePerLine = config->width * CSI_FRAG_INPUT_BYTES_PER_PIXEL;
1090 handle->isDmaBufferCachable = config->isDmaBufferCachable;
1091
1092 /* Get instance from peripheral base address. */
1093 instance = CSI_GetInstance(base);
1094 /* Save the handle in global variables to support the double weak mechanism. */
1095 s_csiHandle[instance] = handle;
1096
1097 s_csiIsr = CSI_FragModeTransferHandleIRQ;
1098
1099 (void)EnableIRQ(s_csiIRQ[instance]);
1100
1101 /* Configure CSICR1. CSICR1 has been reset to the default value, so could write it directly. */
1102 reg = ((uint32_t)config->workMode) | config->polarityFlags | CSI_CR1_FCC_MASK;
1103
1104 if (config->useExtVsync)
1105 {
1106 reg |= CSI_CR1_EXT_VSYNC_MASK;
1107 }
1108
1109 CSI_REG_CR1(base) = reg;
1110
1111 /* No stride. */
1112 CSI_REG_FBUF_PARA(base) = 0;
1113
1114 /* Enable auto ECC. */
1115 CSI_REG_CR3(base) |= CSI_CR3_ECC_AUTO_EN_MASK;
1116
1117 /*
1118 * For better performance.
1119 * The DMA burst size could be set to 16 * 8 byte, 8 * 8 byte, or 4 * 8 byte,
1120 * choose the best burst size based on bytes per line.
1121 */
1122 if (0U == (imgWidth_Bytes % (8U * 16U)))
1123 {
1124 CSI_REG_CR2(base) = CSI_CR2_DMA_BURST_TYPE_RFF(3U);
1125 CSI_REG_CR3(base) = (CSI_REG_CR3(base) & ~CSI_CR3_RxFF_LEVEL_MASK) | ((2U << CSI_CR3_RxFF_LEVEL_SHIFT));
1126 }
1127 else if (0U == (imgWidth_Bytes % (8U * 8U)))
1128 {
1129 CSI_REG_CR2(base) = CSI_CR2_DMA_BURST_TYPE_RFF(2U);
1130 CSI_REG_CR3(base) = (CSI_REG_CR3(base) & ~CSI_CR3_RxFF_LEVEL_MASK) | ((1U << CSI_CR3_RxFF_LEVEL_SHIFT));
1131 }
1132 else
1133 {
1134 CSI_REG_CR2(base) = CSI_CR2_DMA_BURST_TYPE_RFF(1U);
1135 CSI_REG_CR3(base) = (CSI_REG_CR3(base) & ~CSI_CR3_RxFF_LEVEL_MASK) | ((0U << CSI_CR3_RxFF_LEVEL_SHIFT));
1136 }
1137
1138 CSI_REG_DMASA_FB1(base) = CSI_ADDR_CPU_2_IP(config->dmaBufferAddr0);
1139 CSI_REG_DMASA_FB2(base) = CSI_ADDR_CPU_2_IP(config->dmaBufferAddr1);
1140
1141 if (handle->isDmaBufferCachable)
1142 {
1143 DCACHE_CleanInvalidateByRange(
1144 config->dmaBufferAddr0,
1145 (uint32_t)config->dmaBufferLine * (uint32_t)config->width * CSI_FRAG_INPUT_BYTES_PER_PIXEL);
1146 DCACHE_CleanInvalidateByRange(
1147 config->dmaBufferAddr1,
1148 (uint32_t)config->dmaBufferLine * (uint32_t)config->width * CSI_FRAG_INPUT_BYTES_PER_PIXEL);
1149 }
1150
1151 return kStatus_Success;
1152 }
1153
1154 /*!
1155 * brief Start to capture a image.
1156 *
1157 * param base CSI peripheral base address.
1158 * param handle Pointer to the transactional handle.
1159 * param config Pointer to the capture configuration.
1160 *
1161 * retval kStatus_Success Initialize successfully.
1162 * retval kStatus_InvalidArgument Initialize failed because of invalid argument.
1163 */
CSI_FragModeTransferCaptureImage(CSI_Type * base,csi_frag_handle_t * handle,const csi_frag_capture_config_t * config)1164 status_t CSI_FragModeTransferCaptureImage(CSI_Type *base,
1165 csi_frag_handle_t *handle,
1166 const csi_frag_capture_config_t *config)
1167 {
1168 assert(NULL != config);
1169
1170 uint16_t windowWidth;
1171
1172 /*
1173 * If no special window setting, capture full frame.
1174 * If capture window, then capture 1 one each fragment.
1175 */
1176 if (config->window != NULL)
1177 {
1178 handle->windowULX = config->window->windowULX;
1179 handle->windowULY = config->window->windowULY;
1180 handle->windowLRX = config->window->windowLRX;
1181 handle->windowLRY = config->window->windowLRY;
1182 handle->linePerFrag = 1;
1183 }
1184 else
1185 {
1186 handle->windowULX = 0;
1187 handle->windowULY = 0;
1188 handle->windowLRX = handle->width - 1U;
1189 handle->windowLRY = handle->height - 1U;
1190 handle->linePerFrag = handle->maxLinePerFrag;
1191 }
1192
1193 windowWidth = handle->windowLRX - handle->windowULX + 1U;
1194
1195 if (config->outputGrayScale)
1196 {
1197 /* When output format is gray, the window width must be multiple value of 8. */
1198 if (windowWidth % 8U != 0U)
1199 {
1200 return kStatus_InvalidArgument;
1201 }
1202
1203 handle->datBytePerLine = windowWidth;
1204 if (handle->inputFormat == kCSI_FragInputYUYV)
1205 {
1206 handle->copyFunc = CSI_ExtractYFromYUYV;
1207 }
1208 else
1209 {
1210 handle->copyFunc = CSI_ExtractYFromUYVY;
1211 }
1212 }
1213 else
1214 {
1215 handle->datBytePerLine = windowWidth * CSI_FRAG_INPUT_BYTES_PER_PIXEL;
1216 handle->copyFunc = CSI_MemCopy;
1217 }
1218
1219 handle->dmaCurLine = 0;
1220 handle->outputBuffer = (uint32_t)config->buffer;
1221 handle->datCurWriteAddr = (uint32_t)config->buffer;
1222
1223 /* Image parameter. */
1224 CSI_REG_IMAG_PARA(base) =
1225 (((uint32_t)handle->width * CSI_FRAG_INPUT_BYTES_PER_PIXEL) << CSI_IMAG_PARA_IMAGE_WIDTH_SHIFT) |
1226 ((uint32_t)(handle->linePerFrag) << CSI_IMAG_PARA_IMAGE_HEIGHT_SHIFT);
1227
1228 /*
1229 * Write to memory from first completed frame.
1230 * DMA base addr switch at dma transfer done.
1231 */
1232 CSI_REG_CR18(base) = (CSI_REG_CR18(base) & ~CSI_CR18_MASK_OPTION_MASK) | CSI_CR18_MASK_OPTION(0);
1233
1234 CSI_EnableInterrupts(base, (uint32_t)kCSI_StartOfFrameInterruptEnable |
1235 (uint32_t)kCSI_RxBuffer1DmaDoneInterruptEnable |
1236 (uint32_t)kCSI_RxBuffer0DmaDoneInterruptEnable);
1237
1238 return kStatus_Success;
1239 }
1240
1241 /*!
1242 * brief Abort image capture.
1243 *
1244 * Abort image capture initialized by ref CSI_FragModeTransferCaptureImage.
1245 *
1246 * param base CSI peripheral base address.
1247 * param handle Pointer to the transactional handle.
1248 */
CSI_FragModeTransferAbortCaptureImage(CSI_Type * base,csi_frag_handle_t * handle)1249 void CSI_FragModeTransferAbortCaptureImage(CSI_Type *base, csi_frag_handle_t *handle)
1250 {
1251 CSI_Stop(base);
1252 CSI_DisableInterrupts(base, (uint32_t)kCSI_StartOfFrameInterruptEnable |
1253 (uint32_t)kCSI_RxBuffer1DmaDoneInterruptEnable |
1254 (uint32_t)kCSI_RxBuffer0DmaDoneInterruptEnable);
1255 }
1256
1257 /*!
1258 * brief CSI IRQ handle function.
1259 *
1260 * This function handles the CSI IRQ request to work with CSI driver fragment mode
1261 * APIs.
1262 *
1263 * param base CSI peripheral base address.
1264 * param handle CSI handle pointer.
1265 */
CSI_FragModeTransferHandleIRQ(CSI_Type * base,csi_frag_handle_t * handle)1266 void CSI_FragModeTransferHandleIRQ(CSI_Type *base, csi_frag_handle_t *handle)
1267 {
1268 uint32_t csisr = CSI_REG_SR(base);
1269 uint32_t dmaBufAddr;
1270 uint16_t line;
1271 pvoid_to_u32_t memSrc;
1272 pvoid_to_u32_t memDest;
1273
1274 /* Clear the error flags. */
1275 CSI_REG_SR(base) = csisr;
1276
1277 /* Start of frame, clear the FIFO and start receiving. */
1278 if (0U != (csisr & (uint32_t)kCSI_StartOfFrameFlag))
1279 {
1280 /* Reflash the DMA and enable RX DMA request. */
1281 CSI_REG_CR3(base) |= (CSI_CR3_DMA_REFLASH_RFF_MASK | CSI_CR3_DMA_REQ_EN_RFF_MASK);
1282 CSI_Start(base);
1283 handle->dmaCurLine = 0;
1284 handle->datCurWriteAddr = handle->outputBuffer;
1285 }
1286 else if ((csisr & (CSI_SR_DMA_TSF_DONE_FB2_MASK | CSI_SR_DMA_TSF_DONE_FB1_MASK)) != 0U)
1287 {
1288 if ((csisr & CSI_SR_DMA_TSF_DONE_FB1_MASK) == CSI_SR_DMA_TSF_DONE_FB1_MASK)
1289 {
1290 dmaBufAddr = CSI_REG_DMASA_FB1(base);
1291 }
1292 else
1293 {
1294 dmaBufAddr = CSI_REG_DMASA_FB2(base);
1295 }
1296
1297 dmaBufAddr = CSI_ADDR_IP_2_CPU(dmaBufAddr);
1298
1299 if (handle->isDmaBufferCachable)
1300 {
1301 DCACHE_InvalidateByRange(dmaBufAddr, (uint32_t)handle->dmaBytePerLine * (uint32_t)handle->linePerFrag);
1302 }
1303
1304 /* Copy from DMA buffer to user data buffer. */
1305 dmaBufAddr += ((uint32_t)handle->windowULX * CSI_FRAG_INPUT_BYTES_PER_PIXEL);
1306
1307 for (line = 0; line < handle->linePerFrag; line++)
1308 {
1309 if (handle->dmaCurLine + line > handle->windowLRY)
1310 {
1311 /* out of window range */
1312 break;
1313 }
1314 else if (handle->dmaCurLine + line >= handle->windowULY)
1315 {
1316 memDest.u32 = handle->datCurWriteAddr;
1317 memSrc.u32 = dmaBufAddr;
1318
1319 handle->copyFunc(memDest.pvoid, memSrc.pvoid, handle->datBytePerLine);
1320 handle->datCurWriteAddr += handle->datBytePerLine;
1321 dmaBufAddr += handle->dmaBytePerLine;
1322 }
1323 else
1324 {
1325 ; /* For MISRA C-2012 Rule 15.7 */
1326 }
1327 }
1328
1329 handle->dmaCurLine += handle->linePerFrag;
1330
1331 if (handle->dmaCurLine >= handle->height)
1332 {
1333 CSI_Stop(base);
1334 CSI_DisableInterrupts(base, (uint32_t)kCSI_StartOfFrameInterruptEnable |
1335 (uint32_t)kCSI_RxBuffer1DmaDoneInterruptEnable |
1336 (uint32_t)kCSI_RxBuffer0DmaDoneInterruptEnable);
1337
1338 /* Image captured. Stop the CSI. */
1339 if (NULL != handle->callback)
1340 {
1341 handle->callback(base, handle, kStatus_CSI_FrameDone, handle->userData);
1342 }
1343 }
1344 }
1345 else
1346 {
1347 }
1348 }
1349 #endif /* CSI_DRIVER_FRAG_MODE */
1350
1351 #if defined(CSI)
1352 void CSI_DriverIRQHandler(void);
CSI_DriverIRQHandler(void)1353 void CSI_DriverIRQHandler(void)
1354 {
1355 s_csiIsr(CSI, s_csiHandle[0]);
1356 SDK_ISR_EXIT_BARRIER;
1357 }
1358 #endif
1359
1360 #if defined(CSI0)
1361 void CSI0_DriverIRQHandler(void);
CSI0_DriverIRQHandler(void)1362 void CSI0_DriverIRQHandler(void)
1363 {
1364 s_csiIsr(CSI, s_csiHandle[0]);
1365 SDK_ISR_EXIT_BARRIER;
1366 }
1367 #endif
1368