1 /*
2  * Copyright 2019-2021, 2023 NXP
3  * All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  */
7 
8 #include "fsl_fbdev.h"
9 
10 /*******************************************************************************
11  * Definitions
12  ******************************************************************************/
13 
14 /*******************************************************************************
15  * Prototypes
16  ******************************************************************************/
17 
18 static void FBDEV_BufferSwitchOffCallback(void *param, void *switchOffBuffer);
19 
20 /*******************************************************************************
21  * Variables
22  ******************************************************************************/
23 
24 /*******************************************************************************
25  * Code
26  ******************************************************************************/
27 
FBDEV_Open(fbdev_t * fbdev,const dc_fb_t * dc,uint8_t layer)28 status_t FBDEV_Open(fbdev_t *fbdev, const dc_fb_t *dc, uint8_t layer)
29 {
30     status_t status;
31 
32     assert(NULL != fbdev);
33 
34     (void)memset(fbdev, 0, sizeof(fbdev_t));
35 
36     fbdev->dc = dc;
37 
38     (void)VIDEO_STACK_Init(&fbdev->fbManager, fbdev->buffers, FBDEV_MAX_FRAME_BUFFER);
39 
40     /* Initialize the display controller. */
41     status = dc->ops->init(dc);
42     if (kStatus_Success != status)
43     {
44         return status;
45     }
46 
47     fbdev->layer = layer;
48 
49     /* Initializes the dc_fb_info_t to the display controller default setting. */
50     status = dc->ops->getLayerDefaultConfig(dc, layer, &fbdev->fbInfo.bufInfo);
51     if (kStatus_Success != status)
52     {
53         return status;
54     }
55 
56     fbdev->semaFramePending = rt_sem_create("fsfp", 0, RT_IPC_FLAG_PRIO);
57     if (NULL == fbdev->semaFramePending)
58     {
59         return kStatus_Fail;
60     }
61 
62     /* No frame pending. */
63     (void)rt_sem_release(fbdev->semaFramePending);
64 
65     dc->ops->setCallback(dc, layer, FBDEV_BufferSwitchOffCallback, (void *)fbdev);
66 
67     return kStatus_Success;
68 }
69 
FBDEV_Close(fbdev_t * fbdev)70 status_t FBDEV_Close(fbdev_t *fbdev)
71 {
72     const dc_fb_t *dc = fbdev->dc;
73 
74     (void)dc->ops->deinit(dc);
75 
76     if (NULL != fbdev->semaFbManager)
77     {
78         rt_sem_delete(fbdev->semaFbManager);
79         fbdev->semaFbManager = NULL;
80     }
81 
82     if (NULL != fbdev->semaFramePending)
83     {
84         rt_sem_delete(fbdev->semaFramePending);
85         fbdev->semaFramePending = NULL;
86     }
87 
88     return kStatus_Success;
89 }
90 
FBDEV_Enable(fbdev_t * fbdev)91 status_t FBDEV_Enable(fbdev_t *fbdev)
92 {
93     status_t status = kStatus_Success;
94 
95     const dc_fb_t *dc = fbdev->dc;
96 
97     if (!fbdev->enabled)
98     {
99         /* Wait for frame buffer sent to display controller video memory. */
100         if ((dc->ops->getProperty(dc) & (uint32_t)kDC_FB_ReserveFrameBuffer) == 0U)
101         {
102             if (RT_EOK != rt_sem_take(fbdev->semaFramePending, RT_WAITING_FOREVER))
103             {
104                 status = kStatus_Fail;
105             }
106         }
107 
108         if (kStatus_Success == status)
109         {
110             /* No frame is pending. */
111             (void)rt_sem_release(fbdev->semaFramePending);
112 
113             status = dc->ops->enableLayer(dc, fbdev->layer);
114 
115             if (kStatus_Success == status)
116             {
117                 fbdev->enabled = true;
118             }
119         }
120     }
121 
122     return status;
123 }
124 
FBDEV_Disable(fbdev_t * fbdev)125 status_t FBDEV_Disable(fbdev_t *fbdev)
126 {
127     status_t status = kStatus_Success;
128 
129     const dc_fb_t *dc = fbdev->dc;
130 
131     if (!fbdev->enabled)
132     {
133         /* Wait until no frame pending. */
134         if (RT_EOK != rt_sem_take(fbdev->semaFramePending, RT_WAITING_FOREVER))
135         {
136             status = kStatus_Fail;
137         }
138 
139         if (kStatus_Success == status)
140         {
141             (void)rt_sem_release(fbdev->semaFramePending);
142 
143             (void)dc->ops->disableLayer(dc, fbdev->layer);
144 
145             fbdev->enabled = false;
146         }
147     }
148 
149     return status;
150 }
151 
FBDEV_GetFrameBufferInfo(fbdev_t * fbdev,fbdev_fb_info_t * info)152 void FBDEV_GetFrameBufferInfo(fbdev_t *fbdev, fbdev_fb_info_t *info)
153 {
154     *info = fbdev->fbInfo;
155 }
156 
FBDEV_SetFrameBufferInfo(fbdev_t * fbdev,fbdev_fb_info_t * info)157 status_t FBDEV_SetFrameBufferInfo(fbdev_t *fbdev, fbdev_fb_info_t *info)
158 {
159     status_t status;
160     const dc_fb_t *dc = fbdev->dc;
161 
162     /* Should only change the frame buffer setting before enabling the fbdev. */
163     if (fbdev->enabled)
164     {
165         return kStatus_Fail;
166     }
167 
168     fbdev->fbInfo = *info;
169 
170     status = dc->ops->setLayerConfig(dc, fbdev->layer, &fbdev->fbInfo.bufInfo);
171 
172     if (kStatus_Success != status)
173     {
174         return status;
175     }
176 
177     fbdev->semaFbManager = rt_sem_create("fsfm", 0, RT_IPC_FLAG_PRIO);
178     if (NULL == fbdev->semaFbManager)
179     {
180         return kStatus_Fail;
181     }
182 
183     for (uint8_t i = 0; i < info->bufferCount; i++)
184     {
185         /* Don't need to disable interrupt for the FB stack operation, because
186            the fbdev is not working, this is the only function to access FB stack.
187          */
188         (void)VIDEO_STACK_Push(&fbdev->fbManager, info->buffers[i]);
189         (void)rt_sem_release(fbdev->semaFbManager);
190     }
191 
192     return kStatus_Success;
193 }
194 
FBDEV_GetFrameBuffer(fbdev_t * fbdev,uint32_t flags)195 void *FBDEV_GetFrameBuffer(fbdev_t *fbdev, uint32_t flags)
196 {
197     rt_uint32_t tick;
198     void *fb;
199 
200     tick = ((flags & (uint32_t)kFBDEV_NoWait) != 0U) ? 0U : RT_WAITING_FOREVER;
201 
202     if (RT_EOK == rt_sem_take(fbdev->semaFbManager, tick))
203     {
204         /* Disable interrupt to protect the FB stack. */
205         rt_enter_critical();
206         (void)VIDEO_STACK_Pop(&fbdev->fbManager, &fb);
207         rt_exit_critical();
208     }
209     else
210     {
211         fb = NULL;
212     }
213 
214     return fb;
215 }
216 
FBDEV_SetFrameBuffer(fbdev_t * fbdev,void * frameBuffer,uint32_t flags)217 status_t FBDEV_SetFrameBuffer(fbdev_t *fbdev, void *frameBuffer, uint32_t flags)
218 {
219     rt_uint32_t tick;
220     const dc_fb_t *dc = fbdev->dc;
221 
222     tick = ((flags & (uint32_t)kFBDEV_NoWait) != 0U) ? 0U : RT_WAITING_FOREVER;
223 
224     if (RT_EOK == rt_sem_take(fbdev->semaFramePending, tick))
225     {
226         return dc->ops->setFrameBuffer(dc, fbdev->layer, frameBuffer);
227     }
228     else
229     {
230         return kStatus_Fail;
231     }
232 }
233 
FBDEV_BufferSwitchOffCallback(void * param,void * switchOffBuffer)234 static void FBDEV_BufferSwitchOffCallback(void *param, void *switchOffBuffer)
235 {
236     fbdev_t *fbdev              = (fbdev_t *)param;
237 
238     /* This function should only be called in ISR, so don't need to protect the FB stack  */
239     (void)VIDEO_STACK_Push(&fbdev->fbManager, switchOffBuffer);
240     rt_sem_release(fbdev->semaFbManager);
241 
242     rt_sem_release(fbdev->semaFramePending);
243 }
244