1 /*
2  * Copyright (c) 2006-2023, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2021-10-18     Meco Man     The first version
9  * 2021-12-24       Rb         Refresh using dma2d
10  */
11 #include <lvgl.h>
12 
13 //#define DRV_DEBUG
14 #define LOG_TAG             "LVGL.port.disp"
15 #include <drv_log.h>
16 
17 #if LV_USE_NXP_SOC
18 #include "fsl_gpio.h"
19 #include "fsl_elcdif.h"
20 #include "fsl_cache.h"
21 #endif
22 
23 /*A static or global variable to store the buffers*/
24 static lv_disp_draw_buf_t disp_buf;
25 
26 static lv_disp_drv_t disp_drv;  /*Descriptor of a display driver*/
27 
28 /* Macros for panel. */
29 #define LCD_FB_BYTE_PER_PIXEL 2
30 #define LCD_POL_FLAGS \
31     (kELCDIF_DataEnableActiveHigh | kELCDIF_VsyncActiveLow | kELCDIF_HsyncActiveLow | kELCDIF_DriveDataOnRisingClkEdge)
32 #define LCD_LCDIF_DATA_BUS kELCDIF_DataBus16Bit
33 
34 /* Back light. */
35 #define LCD_BL_GPIO     GPIO2
36 #define LCD_BL_GPIO_PIN 31
37 
38 #define DEMO_FB_ALIGN LV_ATTRIBUTE_MEM_ALIGN_SIZE
39 #define DISP_BUF_SIZE (((LCD_WIDTH * LCD_HEIGHT * LCD_FB_BYTE_PER_PIXEL) + DEMO_FB_ALIGN - 1) & ~(DEMO_FB_ALIGN - 1))
40 
41 /*******************************************************************************
42  * Variables
43  ******************************************************************************/
44 static volatile bool s_framePending;
45 
46 static lv_disp_drv_t disp_drv; /*Descriptor of a display driver*/
47 
48 SDK_ALIGN(static uint8_t s_frameBuffer[2][DISP_BUF_SIZE], DEMO_FB_ALIGN);
49 
50 static rt_sem_t s_frameSema = RT_NULL;
51 
lcd_fb_flush(lv_disp_drv_t * disp_drv,const lv_area_t * area,lv_color_t * color_p)52 static void lcd_fb_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p)
53 {
54     rt_sem_take(s_frameSema, RT_WAITING_FOREVER);
55 
56     DCACHE_CleanInvalidateByRange((uint32_t)color_p, DISP_BUF_SIZE);
57 
58     ELCDIF_SetNextBufferAddr(LCDIF, (uint32_t)color_p);
59 
60     s_framePending = true;
61 }
62 
DEMO_InitLcdClock(void)63 static void DEMO_InitLcdClock(void)
64 {
65     /*
66      * The desired output frame rate is 60Hz. So the pixel clock frequency is:
67      * (480 + 41 + 4 + 18) * (272 + 10 + 4 + 2) * 60 = 9.2M.
68      * Here set the LCDIF pixel clock to 9.3M.
69      */
70 
71     /*
72      * Initialize the Video PLL.
73      * Video PLL output clock is OSC24M * (loopDivider + (denominator / numerator)) / postDivider = 93MHz.
74      */
75     clock_video_pll_config_t config =
76     {
77         .loopDivider = 31,
78         .postDivider = 8,
79         .numerator   = 0,
80         .denominator = 0,
81     };
82 
83     CLOCK_InitVideoPll(&config);
84 
85     /*
86      * 000 derive clock from PLL2
87      * 001 derive clock from PLL3 PFD3
88      * 010 derive clock from PLL5
89      * 011 derive clock from PLL2 PFD0
90      * 100 derive clock from PLL2 PFD1
91      * 101 derive clock from PLL3 PFD1
92      */
93     CLOCK_SetMux(kCLOCK_LcdifPreMux, 2);
94 
95     CLOCK_SetDiv(kCLOCK_LcdifPreDiv, 4);
96 
97     CLOCK_SetDiv(kCLOCK_LcdifDiv, 1);
98 }
99 
DEMO_InitLcdBackLight(void)100 static void DEMO_InitLcdBackLight(void)
101 {
102     const gpio_pin_config_t config =
103     {
104         kGPIO_DigitalOutput,
105         1,
106         kGPIO_NoIntmode,
107     };
108 
109     /* Backlight. */
110     GPIO_PinInit(LCD_BL_GPIO, LCD_BL_GPIO_PIN, &config);
111 }
112 
113 #if LV_USE_GPU_NXP_PXP
DEMO_CleanInvalidateCache(lv_disp_drv_t * disp_drv)114 static void DEMO_CleanInvalidateCache(lv_disp_drv_t *disp_drv)
115 {
116     SCB_CleanInvalidateDCache();
117 }
118 #endif
119 
DEMO_InitLcd(void)120 static void DEMO_InitLcd(void)
121 {
122     /* Initialize the display. */
123     const elcdif_rgb_mode_config_t config =
124     {
125         .panelWidth    = LCD_WIDTH,
126         .panelHeight   = LCD_HEIGHT,
127         .hsw           = LCD_HSW,
128         .hfp           = LCD_HFP,
129         .hbp           = LCD_HBP,
130         .vsw           = LCD_VSW,
131         .vfp           = LCD_VFP,
132         .vbp           = LCD_VBP,
133         .polarityFlags = LCD_POL_FLAGS,
134         /* lvgl starts render in frame buffer 0, so show frame buffer 1 first. */
135         .bufferAddr  = (uint32_t)s_frameBuffer[1],
136         .pixelFormat = kELCDIF_PixelFormatRGB565,
137         .dataBus     = LCD_LCDIF_DATA_BUS,
138     };
139 
140     /* Clear frame buffer. */
141     rt_memset((void *)s_frameBuffer, 0, sizeof(s_frameBuffer));
142 
143     s_frameSema = rt_sem_create("lvgl_sem", 1, RT_IPC_FLAG_PRIO);
144 
145     if (RT_NULL == s_frameSema)
146     {
147         rt_kprintf("lvgl semaphore create failed\r\n");
148         RT_ASSERT(0);
149     }
150 
151     /* No frame pending. */
152     s_framePending = false;
153 
154     NVIC_SetPriority(LCDIF_IRQn, 3);
155 
156     DEMO_InitLcdClock();
157 
158     ELCDIF_RgbModeInit(LCDIF, &config);
159 
160     ELCDIF_EnableInterrupts(LCDIF, kELCDIF_CurFrameDoneInterruptEnable);
161 
162     NVIC_EnableIRQ(LCDIF_IRQn);
163 
164     ELCDIF_RgbModeStart(LCDIF);
165 
166     DEMO_InitLcdBackLight();
167 }
168 
LCDIF_IRQHandler(void)169 void LCDIF_IRQHandler(void)
170 {
171     rt_interrupt_enter();
172 
173     uint32_t intStatus = ELCDIF_GetInterruptStatus(LCDIF);
174 
175     ELCDIF_ClearInterruptStatus(LCDIF, intStatus);
176 
177     if (s_framePending)
178     {
179         if (intStatus & kELCDIF_CurFrameDone)
180         {
181             /* IMPORTANT!!!
182              * Inform the graphics library that you are ready with the flushing*/
183             lv_disp_flush_ready(&disp_drv);
184 
185             s_framePending = false;
186 
187             rt_sem_release(s_frameSema);
188         }
189     }
190 
191     rt_interrupt_leave();
192 
193     SDK_ISR_EXIT_BARRIER;
194 }
195 
lv_port_disp_init(void)196 void lv_port_disp_init(void)
197 {
198     lv_disp_draw_buf_init(&disp_buf, s_frameBuffer[0], s_frameBuffer[1], LCD_WIDTH * LCD_HEIGHT);
199 
200     /*-------------------------
201      * Initialize your display
202      * -----------------------*/
203     DEMO_InitLcd();
204 
205     /*-----------------------------------
206      * Register the display in LittlevGL
207      *----------------------------------*/
208 
209     lv_disp_drv_init(&disp_drv); /*Basic initialization*/
210 
211     /*Set up the functions to access to your display*/
212 
213     /*Set the resolution of the display*/
214     disp_drv.hor_res = LCD_WIDTH;
215     disp_drv.ver_res = LCD_HEIGHT;
216 
217     /*Used to copy the buffer's content to the display*/
218     disp_drv.flush_cb = lcd_fb_flush;
219 
220 #if LV_USE_GPU_NXP_PXP
221     disp_drv.clean_dcache_cb = DEMO_CleanInvalidateCache;
222 #endif
223 
224     /*Set a display buffer*/
225     disp_drv.draw_buf = &disp_buf;
226 
227     /* Partial refresh */
228     disp_drv.full_refresh = 1;
229 
230     /*Finally register the driver*/
231     lv_disp_drv_register(&disp_drv);
232 }
233