1 /**************************************************************************//**
2 *
3 * @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved.
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * Change Logs:
8 * Date            Author       Notes
9 * 2021-4-13       Wayne        First version
10 *
11 ******************************************************************************/
12 
13 #include <rtconfig.h>
14 
15 #if defined(BSP_USING_VPOST)
16 
17 #include <rthw.h>
18 #include <rtdevice.h>
19 #include <rtdbg.h>
20 #include "NuMicro.h"
21 #include <drv_sys.h>
22 
23 /* Private typedef --------------------------------------------------------------*/
24 
25 #define DEF_VPOST_BUFFER_NUMBER 3
26 
27 typedef enum
28 {
29     eVpost_LCD,
30 #if defined(BSP_USING_VPOST_OSD)
31     eVpost_OSD,
32 #endif
33     eVpost_Cnt
34 } E_VPOST_LAYER;
35 
36 struct nu_vpost
37 {
38     struct rt_device      dev;
39     char                 *name;
40     E_VPOST_LAYER         layer;
41     IRQn_Type             irqn;
42     E_SYS_IPRST           rstidx;
43     E_SYS_IPCLK           clkidx;
44     uint32_t              last_commit;
45     struct rt_device_graphic_info info;
46 };
47 typedef struct nu_vpost *nu_vpost_t;
48 
49 static volatile uint32_t s_u32VSyncBlank = 0;
50 static volatile uint32_t s_u32UnderRun = 0;
51 static struct rt_completion vsync_wq;
52 
53 static struct nu_vpost nu_fbdev[eVpost_Cnt] =
54 {
55     {
56         .name = "lcd",
57         .layer = eVpost_LCD,
58         .irqn = IRQ_LCD,
59         .rstidx = LCDRST,
60         .clkidx = LCDCKEN,
61     }
62 #if defined(BSP_USING_VPOST_OSD)
63     , {
64         .name = "osd",
65         .layer = eVpost_OSD,
66         .irqn = (IRQn_Type) - 1,
67         .rstidx = SYS_IPRST_NA,
68         .clkidx = SYS_IPCLK_NA,
69     }
70 #endif
71 };
72 
nu_lcd_backlight_on(void)73 rt_weak void nu_lcd_backlight_on(void) { }
74 
nu_lcd_backlight_off(void)75 rt_weak void nu_lcd_backlight_off(void) { }
vpost_layer_open(rt_device_t dev,rt_uint16_t oflag)76 static rt_err_t vpost_layer_open(rt_device_t dev, rt_uint16_t oflag)
77 {
78     nu_vpost_t psVpost = (nu_vpost_t)dev;
79     RT_ASSERT(psVpost != RT_NULL);
80 
81     switch (psVpost->layer)
82     {
83     case eVpost_LCD:
84         vpostVAStartTrigger();
85         break;
86 
87 #if defined(BSP_USING_VPOST_OSD)
88     case eVpost_OSD:
89         vpostVAStartTrigger();
90 
91         /* Set scale to 1:1 */
92         vpostOSDScalingCtrl(1, 0, 0);
93 
94 #if (BSP_LCD_BPP==32)
95         vpostOSDSetColMask(0xff, 0xff, 0xff);
96 #else
97         vpostOSDSetColMask(0x1f, 0x3f, 0x1f);
98 #endif
99 
100         /* Enable color key function */
101         vpostOSDSetColKey(0, 0, 0);
102 
103         /* Configure overlay function of OSD to display OSD image */
104         vpostOSDSetOverlay(DISPLAY_OSD, DISPLAY_OSD, 0);
105 
106         vpostOSDEnable();
107         break;
108 #endif
109 
110     default:
111         return -RT_ERROR;
112     }
113 
114     return RT_EOK;
115 }
116 
vpost_layer_close(rt_device_t dev)117 static rt_err_t vpost_layer_close(rt_device_t dev)
118 {
119     nu_vpost_t psVpost = (nu_vpost_t)dev;
120     RT_ASSERT(psVpost != RT_NULL);
121 
122     switch (psVpost->layer)
123     {
124     case eVpost_LCD:
125 #if defined(BSP_USING_VPOST_OSD)
126         if (nu_fbdev[eVpost_OSD].dev.ref_count == 0)
127 #endif
128             vpostVAStopTrigger();
129         break;
130 
131 #if defined(BSP_USING_VPOST_OSD)
132     case eVpost_OSD:
133         vpostOSDDisable();
134         if (nu_fbdev[eVpost_LCD].dev.ref_count == 0)
135         {
136             /* Also stop displaying */
137             vpostVAStopTrigger();
138         }
139         break;
140 #endif
141 
142     default:
143         return -RT_ERROR;
144     }
145 
146     return RT_EOK;
147 }
148 
vpost_layer_control(rt_device_t dev,int cmd,void * args)149 static rt_err_t vpost_layer_control(rt_device_t dev, int cmd, void *args)
150 {
151     nu_vpost_t psVpost = (nu_vpost_t)dev;
152     RT_ASSERT(psVpost != RT_NULL);
153 
154     switch (cmd)
155     {
156     case RTGRAPHIC_CTRL_POWERON:
157     {
158         nu_lcd_backlight_on();
159     }
160     break;
161 
162     case RTGRAPHIC_CTRL_POWEROFF:
163     {
164         nu_lcd_backlight_off();
165     }
166     break;
167 
168     case RTGRAPHIC_CTRL_GET_INFO:
169     {
170         struct rt_device_graphic_info *info = (struct rt_device_graphic_info *) args;
171         RT_ASSERT(info != RT_NULL);
172         rt_memcpy(args, (void *)&psVpost->info, sizeof(struct rt_device_graphic_info));
173     }
174     break;
175 
176     case RTGRAPHIC_CTRL_PAN_DISPLAY:
177     {
178         if (args != RT_NULL)
179         {
180             uint8_t *pu8BufPtr = (uint8_t *)args;
181 
182             psVpost->last_commit = s_u32VSyncBlank;
183 
184             /* Pan display */
185             switch (psVpost->layer)
186             {
187             case eVpost_LCD:
188                 vpostSetFrameBuffer(pu8BufPtr);
189                 break;
190 
191 #if defined(BSP_USING_VPOST_OSD)
192             case eVpost_OSD:
193                 vpostSetOSDBuffer(pu8BufPtr);
194                 break;
195 #endif
196 
197             default:
198                 return -RT_ERROR;
199             }
200 
201         }
202         else
203             return -RT_ERROR;
204     }
205     break;
206 
207     case RTGRAPHIC_CTRL_WAIT_VSYNC:
208     {
209         if (args != RT_NULL)
210             psVpost->last_commit = s_u32VSyncBlank + 1;
211 
212         if (psVpost->last_commit >= s_u32VSyncBlank)
213         {
214             rt_completion_init(&vsync_wq);
215             rt_completion_wait(&vsync_wq, RT_TICK_PER_SECOND / 60);
216         }
217     }
218     break;
219 
220     default:
221         return -RT_ERROR;
222     }
223 
224     return RT_EOK;
225 }
226 
vpost_layer_init(rt_device_t dev)227 static rt_err_t vpost_layer_init(rt_device_t dev)
228 {
229     nu_vpost_t psVpost = (nu_vpost_t)dev;
230     RT_ASSERT(psVpost != RT_NULL);
231 
232     /* Enable VPOST engine clock. */
233     nu_sys_ipclk_enable(LCDCKEN);
234 
235     rt_completion_init(&vsync_wq);
236     outpw(REG_LCM_INT_CS, VPOSTB_UNDERRUN_EN | VPOSTB_DISP_F_EN);
237     outpw(REG_LCM_DCCS, (inpw(REG_LCM_DCCS) | (1 << 4)));
238 
239     return RT_EOK;
240 }
241 
242 
nu_vpost_calculate_fps(void)243 static void nu_vpost_calculate_fps(void)
244 {
245 #define DEF_PERIOD_SEC  10
246     static uint32_t u32LastTick = 0;
247     static uint32_t u32VSyncBlank = 0;
248     static uint32_t u32UnderRun = 0;
249     uint32_t u32CurrTick = rt_tick_get();
250 
251     if ((u32CurrTick - u32LastTick) > (DEF_PERIOD_SEC * RT_TICK_PER_SECOND))
252     {
253         rt_kprintf("VPOST: %d FPS, URPS: %d\n",
254                    (s_u32VSyncBlank - u32VSyncBlank) / DEF_PERIOD_SEC,
255                    (s_u32UnderRun - u32UnderRun) / DEF_PERIOD_SEC);
256         u32LastTick = u32CurrTick;
257         u32VSyncBlank = s_u32VSyncBlank;
258         u32UnderRun = s_u32UnderRun;
259     }
260 }
261 
nu_vpost_isr(int vector,void * param)262 static void nu_vpost_isr(int vector, void *param)
263 {
264     /*
265     #define VPOSTB_DISP_F_INT           ((UINT32)1<<31)
266     #define VPOSTB_DISP_F_STATUS        (1<<30)
267     #define VPOSTB_UNDERRUN_INT         (1<<29)
268     #define VPOSTB_BUS_ERROR_INT        (1<<28)
269     #define VPOSTB_FLY_ERR              (1<<27)
270     #define VPOSTB_UNDERRUN_EN          (1<<1)
271     #define VPOSTB_DISP_F_EN            (1)
272     */
273 
274     uint32_t u32VpostIRQStatus = inpw(REG_LCM_INT_CS);
275     if (u32VpostIRQStatus & VPOSTB_DISP_F_STATUS)
276     {
277         outpw(REG_LCM_INT_CS, inpw(REG_LCM_INT_CS) | VPOSTB_DISP_F_STATUS);
278 
279         s_u32VSyncBlank++;
280         rt_completion_done(&vsync_wq);
281     }
282     else if (u32VpostIRQStatus & VPOSTB_UNDERRUN_INT)
283     {
284         s_u32UnderRun++;
285         outpw(REG_LCM_INT_CS, inpw(REG_LCM_INT_CS) | VPOSTB_UNDERRUN_INT);
286     }
287     else if (u32VpostIRQStatus & VPOSTB_BUS_ERROR_INT)
288     {
289         outpw(REG_LCM_INT_CS, inpw(REG_LCM_INT_CS) | VPOSTB_BUS_ERROR_INT);
290     }
291 
292     nu_vpost_calculate_fps();
293 }
294 
rt_hw_vpost_init(void)295 int rt_hw_vpost_init(void)
296 {
297     int i = -1;
298     rt_err_t ret;
299 
300     VPOST_T *psVpostLcmInst = vpostLCMGetInstance(VPOST_USING_LCD_IDX);
301     RT_ASSERT(psVpostLcmInst != RT_NULL);
302 
303     if ((psVpostLcmInst->u32DevWidth * psVpostLcmInst->u32DevHeight) > (480 * 272))
304     {
305         /* LCD clock is selected from UPLL and divide to 20MHz */
306         outpw(REG_CLK_DIVCTL1, (inpw(REG_CLK_DIVCTL1) & ~0xff1f) | 0xE18);
307 
308         /* LCD clock is selected from UPLL and divide to 30MHz */
309         //outpw(REG_CLK_DIVCTL1, (inpw(REG_CLK_DIVCTL1) & ~0xff1f) | 0x918);
310 
311         /* LCD clock is selected from UPLL and divide to 33.3MHz */
312         //outpw(REG_CLK_DIVCTL1, (inpw(REG_CLK_DIVCTL1) & ~0xff1f) | 0x818);
313     }
314     else
315     {
316         /* LCD clock is selected from UPLL and divide to 10MHz */
317         outpw(REG_CLK_DIVCTL1, (inpw(REG_CLK_DIVCTL1) & ~0xff1f) | 0xE19);
318     }
319 
320     /* Initial LCM */
321     vpostLCMInit(VPOST_USING_LCD_IDX);
322 
323     /* Set scale to 1:1 */
324     vpostVAScalingCtrl(1, 0, 1, 0, VA_SCALE_INTERPOLATION);
325 
326     for (i = eVpost_LCD; i < eVpost_Cnt; i++)
327     {
328         nu_vpost_t psVpost = &nu_fbdev[i];
329         rt_memset((void *)&psVpost->info, 0, sizeof(struct rt_device_graphic_info));
330 
331         /* Register VPOST information */
332         psVpost->info.bits_per_pixel = BSP_LCD_BPP;
333         psVpost->info.pixel_format = (BSP_LCD_BPP == 32) ? RTGRAPHIC_PIXEL_FORMAT_ARGB888 : RTGRAPHIC_PIXEL_FORMAT_RGB565;
334         psVpost->info.pitch = psVpostLcmInst->u32DevWidth * (BSP_LCD_BPP / 8);
335         psVpost->info.width = psVpostLcmInst->u32DevWidth;
336         psVpost->info.height = psVpostLcmInst->u32DevHeight;
337 
338         /* Get pointer of video frame buffer */
339         /* Set display color depth */
340         /* Note: before get pointer of frame buffer, must set display color depth first */
341         if (psVpost->layer == eVpost_LCD)
342         {
343 #if (BSP_LCD_BPP==32)
344             vpostSetVASrc(VA_SRC_RGB888);
345 #else
346             vpostSetVASrc(VA_SRC_RGB565);
347 #endif
348             psVpost->info.framebuffer = (rt_uint8_t *)vpostGetMultiFrameBuffer(DEF_VPOST_BUFFER_NUMBER);
349         }
350 #if defined(BSP_USING_VPOST_OSD)
351         else if (psVpost->layer == eVpost_OSD)
352         {
353             vpostOSDSetWindow(0, 0, psVpost->info.width, psVpost->info.height);
354 
355 #if (BSP_LCD_BPP==32)
356             vpostSetOSDSrc(OSD_SRC_RGB888);
357 #else
358             vpostSetOSDSrc(OSD_SRC_RGB565);
359 #endif
360             psVpost->info.framebuffer = (rt_uint8_t *)vpostGetMultiOSDBuffer(DEF_VPOST_BUFFER_NUMBER);
361         }
362 #endif
363 
364         if (psVpost->info.framebuffer == NULL)
365         {
366             rt_kprintf("Fail to get VRAM buffer.\n");
367             RT_ASSERT(0);
368         }
369         else
370         {
371             uint32_t u32FBSize = psVpost->info.pitch * psVpostLcmInst->u32DevHeight;
372             psVpost->info.smem_len = u32FBSize * DEF_VPOST_BUFFER_NUMBER;
373             rt_memset(psVpost->info.framebuffer, 0, u32FBSize);
374         }
375 
376         /* Register member functions of lcd device */
377         psVpost->dev.type = RT_Device_Class_Graphic;
378         psVpost->dev.init = vpost_layer_init;
379         psVpost->dev.open = vpost_layer_open;
380         psVpost->dev.close = vpost_layer_close;
381         psVpost->dev.control = vpost_layer_control;
382 
383         /* Register graphic device driver */
384         ret = rt_device_register(&psVpost->dev, psVpost->name, RT_DEVICE_FLAG_RDWR);
385         RT_ASSERT(ret == RT_EOK);
386 
387         if (psVpost->layer == eVpost_LCD)
388         {
389             rt_hw_interrupt_install(psVpost->irqn, nu_vpost_isr,  psVpost, psVpost->name);
390             rt_hw_interrupt_umask(psVpost->irqn);
391         }
392 
393         rt_kprintf("%s's fbmem at 0x%08x.\n", psVpost->name, psVpost->info.framebuffer);
394     }
395 
396     /* For saving memory bandwidth. */
397     vpostLCMDeinit();
398 
399     return (int)ret;
400 }
401 INIT_DEVICE_EXPORT(rt_hw_vpost_init);
402 
403 
404 /* Support "vpost_set_osd_colkey" command line in msh mode */
vpost_set_osd_colkey(int argc,char ** argv)405 static rt_err_t vpost_set_osd_colkey(int argc, char **argv)
406 {
407     rt_uint32_t index, len, arg[4];
408 
409     rt_memset(arg, 0, sizeof(arg));
410     len = (argc >= 4) ? 4 : argc;
411 
412     for (index = 0; index < (len - 1); index ++)
413     {
414         arg[index] = atol(argv[index + 1]);
415     }
416 
417     /* Enable color key function */
418     vpostOSDSetColKey(arg[0], arg[1], arg[2]);
419 
420     /* Configure overlay function of OSD to display VIDEO image */
421     vpostOSDSetOverlay(DISPLAY_VIDEO, DISPLAY_OSD, 0);
422 
423     return 0;
424 }
425 MSH_CMD_EXPORT(vpost_set_osd_colkey, e.g: vpost_set_osd_colkey R G B);
426 
427 /* Support "vpost_show_layer" command line in msh mode */
vpost_show_layer(int argc,char ** argv)428 static rt_err_t vpost_show_layer(int argc, char **argv)
429 {
430     rt_uint32_t index, len, arg[2];
431     nu_vpost_t psVpostLayer;
432 
433     rt_memset(arg, 0, sizeof(arg));
434     len = (argc >= 2) ? 2 : argc;
435 
436     for (index = 0; index < (len - 1); index ++)
437     {
438         arg[index] = atol(argv[index + 1]);
439     }
440     psVpostLayer = &nu_fbdev[arg[0]];
441 
442     return rt_device_open(&psVpostLayer->dev, RT_DEVICE_FLAG_RDWR);
443 }
444 MSH_CMD_EXPORT(vpost_show_layer, e.g: vpost_show_layer layer);
445 
446 /* Support "vpost_hide_layer" command line in msh mode */
vpost_hide_layer(int argc,char ** argv)447 static rt_err_t vpost_hide_layer(int argc, char **argv)
448 {
449     rt_uint32_t index, len, arg[2];
450     nu_vpost_t psVpostLayer;
451 
452     rt_memset(arg, 0, sizeof(arg));
453     len = (argc >= 2) ? 2 : argc;
454 
455     for (index = 0; index < (len - 1); index ++)
456     {
457         arg[index] = atol(argv[index + 1]);
458     }
459     psVpostLayer = &nu_fbdev[arg[0]];
460 
461     return rt_device_close(&psVpostLayer->dev);
462 }
463 MSH_CMD_EXPORT(vpost_hide_layer, e.g: vpost_hide_layer layer);
464 
465 /* Support "vpost_fill_color" command line in msh mode */
vpost_fill_color(int argc,char ** argv)466 static rt_err_t vpost_fill_color(int argc, char **argv)
467 {
468     rt_uint32_t index, len, arg[5];
469     nu_vpost_t psVpostLayer;
470 
471     rt_memset(arg, 0, sizeof(arg));
472     len = (argc >= 5) ? 5 : argc;
473 
474     for (index = 0; index < (len - 1); index ++)
475     {
476         arg[index] = atol(argv[index + 1]);
477     }
478 
479     psVpostLayer = &nu_fbdev[arg[0]];
480 
481     if (psVpostLayer->info.framebuffer != RT_NULL)
482     {
483         int i;
484         uint32_t fill_num = psVpostLayer->info.height * psVpostLayer->info.width;
485         uint32_t *fbmem_start = (uint32_t *)psVpostLayer->info.framebuffer;
486         uint32_t color = (arg[1] << 16) | (arg[2] << 8) | arg[3] ;
487         for (i = 0; i < fill_num; i++)
488         {
489             rt_memcpy((void *)&fbmem_start[i], &color, (psVpostLayer->info.bits_per_pixel / 8));
490         }
491     }
492     return 0;
493 }
494 MSH_CMD_EXPORT(vpost_fill_color, e.g: vpost_fill_color layer R G B);
495 
496 #endif /* if defined(BSP_USING_VPOST) */
497