/**************************************************************************//** * * @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2021-4-13 Wayne First version * ******************************************************************************/ #include #if defined(BSP_USING_VPOST) #include #include #include #include "NuMicro.h" #include /* Private typedef --------------------------------------------------------------*/ #define DEF_VPOST_BUFFER_NUMBER 3 typedef enum { eVpost_LCD, #if defined(BSP_USING_VPOST_OSD) eVpost_OSD, #endif eVpost_Cnt } E_VPOST_LAYER; struct nu_vpost { struct rt_device dev; char *name; E_VPOST_LAYER layer; IRQn_Type irqn; E_SYS_IPRST rstidx; E_SYS_IPCLK clkidx; uint32_t last_commit; struct rt_device_graphic_info info; }; typedef struct nu_vpost *nu_vpost_t; static volatile uint32_t s_u32VSyncBlank = 0; static volatile uint32_t s_u32UnderRun = 0; static struct rt_completion vsync_wq; static struct nu_vpost nu_fbdev[eVpost_Cnt] = { { .name = "lcd", .layer = eVpost_LCD, .irqn = IRQ_LCD, .rstidx = LCDRST, .clkidx = LCDCKEN, } #if defined(BSP_USING_VPOST_OSD) , { .name = "osd", .layer = eVpost_OSD, .irqn = (IRQn_Type) - 1, .rstidx = SYS_IPRST_NA, .clkidx = SYS_IPCLK_NA, } #endif }; RT_WEAK void nu_lcd_backlight_on(void) { } RT_WEAK void nu_lcd_backlight_off(void) { } static rt_err_t vpost_layer_open(rt_device_t dev, rt_uint16_t oflag) { nu_vpost_t psVpost = (nu_vpost_t)dev; RT_ASSERT(psVpost != RT_NULL); switch (psVpost->layer) { case eVpost_LCD: vpostVAStartTrigger(); break; #if defined(BSP_USING_VPOST_OSD) case eVpost_OSD: vpostVAStartTrigger(); /* Set scale to 1:1 */ vpostOSDScalingCtrl(1, 0, 0); #if (BSP_LCD_BPP==32) vpostOSDSetColMask(0xff, 0xff, 0xff); #else vpostOSDSetColMask(0x1f, 0x3f, 0x1f); #endif /* Enable color key function */ vpostOSDSetColKey(0, 0, 0); /* Configure overlay function of OSD to display OSD image */ vpostOSDSetOverlay(DISPLAY_OSD, DISPLAY_OSD, 0); vpostOSDEnable(); break; #endif default: return -RT_ERROR; } return RT_EOK; } static rt_err_t vpost_layer_close(rt_device_t dev) { nu_vpost_t psVpost = (nu_vpost_t)dev; RT_ASSERT(psVpost != RT_NULL); switch (psVpost->layer) { case eVpost_LCD: #if defined(BSP_USING_VPOST_OSD) if (nu_fbdev[eVpost_OSD].dev.ref_count == 0) #endif vpostVAStopTrigger(); break; #if defined(BSP_USING_VPOST_OSD) case eVpost_OSD: vpostOSDDisable(); if (nu_fbdev[eVpost_LCD].dev.ref_count == 0) { /* Also stop displaying */ vpostVAStopTrigger(); } break; #endif default: return -RT_ERROR; } return RT_EOK; } static rt_err_t vpost_layer_control(rt_device_t dev, int cmd, void *args) { nu_vpost_t psVpost = (nu_vpost_t)dev; RT_ASSERT(psVpost != RT_NULL); switch (cmd) { case RTGRAPHIC_CTRL_POWERON: { nu_lcd_backlight_on(); } break; case RTGRAPHIC_CTRL_POWEROFF: { nu_lcd_backlight_off(); } break; case RTGRAPHIC_CTRL_GET_INFO: { struct rt_device_graphic_info *info = (struct rt_device_graphic_info *) args; RT_ASSERT(info != RT_NULL); rt_memcpy(args, (void *)&psVpost->info, sizeof(struct rt_device_graphic_info)); } break; case RTGRAPHIC_CTRL_PAN_DISPLAY: { if (args != RT_NULL) { uint8_t *pu8BufPtr = (uint8_t *)args; psVpost->last_commit = s_u32VSyncBlank; /* Pan display */ switch (psVpost->layer) { case eVpost_LCD: vpostSetFrameBuffer(pu8BufPtr); break; #if defined(BSP_USING_VPOST_OSD) case eVpost_OSD: vpostSetOSDBuffer(pu8BufPtr); break; #endif default: return -RT_ERROR; } } else return -RT_ERROR; } break; case RTGRAPHIC_CTRL_WAIT_VSYNC: { if (args != RT_NULL) psVpost->last_commit = s_u32VSyncBlank + 1; if (psVpost->last_commit >= s_u32VSyncBlank) { rt_completion_init(&vsync_wq); rt_completion_wait(&vsync_wq, RT_TICK_PER_SECOND / 60); } } break; default: return -RT_ERROR; } return RT_EOK; } static rt_err_t vpost_layer_init(rt_device_t dev) { nu_vpost_t psVpost = (nu_vpost_t)dev; RT_ASSERT(psVpost != RT_NULL); /* Enable VPOST engine clock. */ nu_sys_ipclk_enable(LCDCKEN); rt_completion_init(&vsync_wq); outpw(REG_LCM_INT_CS, VPOSTB_UNDERRUN_EN | VPOSTB_DISP_F_EN); outpw(REG_LCM_DCCS, (inpw(REG_LCM_DCCS) | (1 << 4))); return RT_EOK; } static void nu_vpost_calculate_fps(void) { #define DEF_PERIOD_SEC 10 static uint32_t u32LastTick=0; static uint32_t u32VSyncBlank=0; static uint32_t u32UnderRun=0; uint32_t u32CurrTick = rt_tick_get(); if ( (u32CurrTick-u32LastTick) > (DEF_PERIOD_SEC*RT_TICK_PER_SECOND) ) { rt_kprintf("VPOST: %d FPS, URPS: %d\n", (s_u32VSyncBlank-u32VSyncBlank)/DEF_PERIOD_SEC, (s_u32UnderRun-u32UnderRun)/DEF_PERIOD_SEC); u32LastTick = u32CurrTick; u32VSyncBlank = s_u32VSyncBlank; u32UnderRun = s_u32UnderRun; } } static void nu_vpost_isr(int vector, void *param) { /* #define VPOSTB_DISP_F_INT ((UINT32)1<<31) #define VPOSTB_DISP_F_STATUS (1<<30) #define VPOSTB_UNDERRUN_INT (1<<29) #define VPOSTB_BUS_ERROR_INT (1<<28) #define VPOSTB_FLY_ERR (1<<27) #define VPOSTB_UNDERRUN_EN (1<<1) #define VPOSTB_DISP_F_EN (1) */ uint32_t u32VpostIRQStatus = inpw(REG_LCM_INT_CS); if (u32VpostIRQStatus & VPOSTB_DISP_F_STATUS) { outpw(REG_LCM_INT_CS, inpw(REG_LCM_INT_CS) | VPOSTB_DISP_F_STATUS); s_u32VSyncBlank++; rt_completion_done(&vsync_wq); } else if (u32VpostIRQStatus & VPOSTB_UNDERRUN_INT) { s_u32UnderRun++; outpw(REG_LCM_INT_CS, inpw(REG_LCM_INT_CS) | VPOSTB_UNDERRUN_INT); } else if (u32VpostIRQStatus & VPOSTB_BUS_ERROR_INT) { outpw(REG_LCM_INT_CS, inpw(REG_LCM_INT_CS) | VPOSTB_BUS_ERROR_INT); } nu_vpost_calculate_fps(); } int rt_hw_vpost_init(void) { int i = -1; rt_err_t ret; VPOST_T *psVpostLcmInst = vpostLCMGetInstance(VPOST_USING_LCD_IDX); RT_ASSERT(psVpostLcmInst != RT_NULL); if ((psVpostLcmInst->u32DevWidth * psVpostLcmInst->u32DevHeight) > (480 * 272)) { /* LCD clock is selected from UPLL and divide to 20MHz */ outpw(REG_CLK_DIVCTL1, (inpw(REG_CLK_DIVCTL1) & ~0xff1f) | 0xE18); /* LCD clock is selected from UPLL and divide to 30MHz */ //outpw(REG_CLK_DIVCTL1, (inpw(REG_CLK_DIVCTL1) & ~0xff1f) | 0x918); /* LCD clock is selected from UPLL and divide to 33.3MHz */ //outpw(REG_CLK_DIVCTL1, (inpw(REG_CLK_DIVCTL1) & ~0xff1f) | 0x818); } else { /* LCD clock is selected from UPLL and divide to 10MHz */ outpw(REG_CLK_DIVCTL1, (inpw(REG_CLK_DIVCTL1) & ~0xff1f) | 0xE19); } /* Initial LCM */ vpostLCMInit(VPOST_USING_LCD_IDX); /* Set scale to 1:1 */ vpostVAScalingCtrl(1, 0, 1, 0, VA_SCALE_INTERPOLATION); for (i = eVpost_LCD; i < eVpost_Cnt; i++) { nu_vpost_t psVpost = &nu_fbdev[i]; rt_memset((void *)&psVpost->info, 0, sizeof(struct rt_device_graphic_info)); /* Register VPOST information */ psVpost->info.bits_per_pixel = BSP_LCD_BPP; psVpost->info.pixel_format = (BSP_LCD_BPP == 32) ? RTGRAPHIC_PIXEL_FORMAT_ARGB888 : RTGRAPHIC_PIXEL_FORMAT_RGB565; psVpost->info.pitch = psVpostLcmInst->u32DevWidth * (BSP_LCD_BPP / 8); psVpost->info.width = psVpostLcmInst->u32DevWidth; psVpost->info.height = psVpostLcmInst->u32DevHeight; /* Get pointer of video frame buffer */ /* Set display color depth */ /* Note: before get pointer of frame buffer, must set display color depth first */ if (psVpost->layer == eVpost_LCD) { #if (BSP_LCD_BPP==32) vpostSetVASrc(VA_SRC_RGB888); #else vpostSetVASrc(VA_SRC_RGB565); #endif psVpost->info.framebuffer = (rt_uint8_t *)vpostGetMultiFrameBuffer(DEF_VPOST_BUFFER_NUMBER); } #if defined(BSP_USING_VPOST_OSD) else if (psVpost->layer == eVpost_OSD) { vpostOSDSetWindow(0, 0, psVpost->info.width, psVpost->info.height); #if (BSP_LCD_BPP==32) vpostSetOSDSrc(OSD_SRC_RGB888); #else vpostSetOSDSrc(OSD_SRC_RGB565); #endif psVpost->info.framebuffer = (rt_uint8_t *)vpostGetMultiOSDBuffer(DEF_VPOST_BUFFER_NUMBER); } #endif if (psVpost->info.framebuffer == NULL) { rt_kprintf("Fail to get VRAM buffer.\n"); RT_ASSERT(0); } else { uint32_t u32FBSize = psVpost->info.pitch * psVpostLcmInst->u32DevHeight; psVpost->info.smem_len = u32FBSize * DEF_VPOST_BUFFER_NUMBER; rt_memset(psVpost->info.framebuffer, 0, u32FBSize); } /* Register member functions of lcd device */ psVpost->dev.type = RT_Device_Class_Graphic; psVpost->dev.init = vpost_layer_init; psVpost->dev.open = vpost_layer_open; psVpost->dev.close = vpost_layer_close; psVpost->dev.control = vpost_layer_control; /* Register graphic device driver */ ret = rt_device_register(&psVpost->dev, psVpost->name, RT_DEVICE_FLAG_RDWR); RT_ASSERT(ret == RT_EOK); if (psVpost->layer == eVpost_LCD) { rt_hw_interrupt_install(psVpost->irqn, nu_vpost_isr, psVpost, psVpost->name); rt_hw_interrupt_umask(psVpost->irqn); } rt_kprintf("%s's fbmem at 0x%08x.\n", psVpost->name, psVpost->info.framebuffer); } /* For saving memory bandwidth. */ vpostLCMDeinit(); return (int)ret; } INIT_DEVICE_EXPORT(rt_hw_vpost_init); /* Support "vpost_set_osd_colkey" command line in msh mode */ static rt_err_t vpost_set_osd_colkey(int argc, char **argv) { rt_uint32_t index, len, arg[4]; rt_memset(arg, 0, sizeof(arg)); len = (argc >= 4) ? 4 : argc; for (index = 0; index < (len - 1); index ++) { arg[index] = atol(argv[index + 1]); } /* Enable color key function */ vpostOSDSetColKey(arg[0], arg[1], arg[2]); /* Configure overlay function of OSD to display VIDEO image */ vpostOSDSetOverlay(DISPLAY_VIDEO, DISPLAY_OSD, 0); return 0; } MSH_CMD_EXPORT(vpost_set_osd_colkey, e.g: vpost_set_osd_colkey R G B); /* Support "vpost_show_layer" command line in msh mode */ static rt_err_t vpost_show_layer(int argc, char **argv) { rt_uint32_t index, len, arg[2]; nu_vpost_t psVpostLayer; rt_memset(arg, 0, sizeof(arg)); len = (argc >= 2) ? 2 : argc; for (index = 0; index < (len - 1); index ++) { arg[index] = atol(argv[index + 1]); } psVpostLayer = &nu_fbdev[arg[0]]; return rt_device_open(&psVpostLayer->dev, RT_DEVICE_FLAG_RDWR); } MSH_CMD_EXPORT(vpost_show_layer, e.g: vpost_show_layer layer); /* Support "vpost_hide_layer" command line in msh mode */ static rt_err_t vpost_hide_layer(int argc, char **argv) { rt_uint32_t index, len, arg[2]; nu_vpost_t psVpostLayer; rt_memset(arg, 0, sizeof(arg)); len = (argc >= 2) ? 2 : argc; for (index = 0; index < (len - 1); index ++) { arg[index] = atol(argv[index + 1]); } psVpostLayer = &nu_fbdev[arg[0]]; return rt_device_close(&psVpostLayer->dev); } MSH_CMD_EXPORT(vpost_hide_layer, e.g: vpost_hide_layer layer); /* Support "vpost_fill_color" command line in msh mode */ static rt_err_t vpost_fill_color(int argc, char **argv) { rt_uint32_t index, len, arg[5]; nu_vpost_t psVpostLayer; rt_memset(arg, 0, sizeof(arg)); len = (argc >= 5) ? 5 : argc; for (index = 0; index < (len - 1); index ++) { arg[index] = atol(argv[index + 1]); } psVpostLayer = &nu_fbdev[arg[0]]; if (psVpostLayer->info.framebuffer != RT_NULL) { int i; uint32_t fill_num = psVpostLayer->info.height * psVpostLayer->info.width; uint32_t *fbmem_start = (uint32_t *)psVpostLayer->info.framebuffer; uint32_t color = (arg[1] << 16) | (arg[2] << 8) | arg[3] ; for (i = 0; i < fill_num; i++) { rt_memcpy((void *)&fbmem_start[i], &color, (psVpostLayer->info.bits_per_pixel / 8)); } } return 0; } MSH_CMD_EXPORT(vpost_fill_color, e.g: vpost_fill_color layer R G B); #endif /* if defined(BSP_USING_VPOST) */