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