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