2021-05-12 19:15:17 +08:00
|
|
|
/**************************************************************************//**
|
|
|
|
*
|
|
|
|
* @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 <rtconfig.h>
|
|
|
|
|
|
|
|
#if defined(BSP_USING_VPOST)
|
|
|
|
|
|
|
|
#include <rthw.h>
|
|
|
|
#include <rtdevice.h>
|
|
|
|
#include <rtdbg.h>
|
|
|
|
#include "NuMicro.h"
|
|
|
|
#include <drv_sys.h>
|
|
|
|
|
|
|
|
/* Private typedef --------------------------------------------------------------*/
|
|
|
|
|
2021-12-28 13:37:04 +08:00
|
|
|
#define DEF_VPOST_BUFFER_NUMBER 3
|
2021-12-17 17:20:33 +08:00
|
|
|
|
2021-05-12 19:15:17 +08:00
|
|
|
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;
|
|
|
|
struct rt_device_graphic_info info;
|
|
|
|
};
|
|
|
|
typedef struct nu_vpost *nu_vpost_t;
|
|
|
|
|
2021-12-17 17:20:33 +08:00
|
|
|
static volatile uint32_t g_u32VSyncBlank = 0;
|
|
|
|
static volatile uint32_t g_u32VSyncLastCommit = 0;
|
|
|
|
static struct rt_completion vsync_wq;
|
|
|
|
|
2021-05-12 19:15:17 +08:00
|
|
|
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
|
|
|
|
};
|
|
|
|
|
|
|
|
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 (LCM_USING_BPP==4)
|
|
|
|
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_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;
|
|
|
|
|
2021-12-28 13:37:04 +08:00
|
|
|
case RTGRAPHIC_CTRL_PAN_DISPLAY:
|
2021-12-17 17:20:33 +08:00
|
|
|
{
|
|
|
|
if (args != RT_NULL)
|
|
|
|
{
|
|
|
|
uint8_t *pu8BufPtr = (uint8_t *)args;
|
2021-12-28 13:37:04 +08:00
|
|
|
|
2021-12-17 17:20:33 +08:00
|
|
|
g_u32VSyncLastCommit = g_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;
|
|
|
|
|
2021-12-28 13:37:04 +08:00
|
|
|
case RTGRAPHIC_CTRL_WAIT_VSYNC:
|
|
|
|
{
|
|
|
|
if (args != RT_NULL)
|
|
|
|
g_u32VSyncLastCommit = g_u32VSyncBlank+1;
|
|
|
|
|
|
|
|
if (g_u32VSyncLastCommit >= g_u32VSyncBlank)
|
|
|
|
{
|
|
|
|
rt_completion_init(&vsync_wq);
|
|
|
|
rt_completion_wait(&vsync_wq, RT_TICK_PER_SECOND / 60);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2021-05-12 19:15:17 +08:00
|
|
|
default:
|
2021-12-28 13:37:04 +08:00
|
|
|
return -RT_ERROR;
|
2021-05-12 19:15:17 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2021-12-17 17:20:33 +08:00
|
|
|
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)));
|
|
|
|
|
2021-05-12 19:15:17 +08:00
|
|
|
return RT_EOK;
|
|
|
|
}
|
|
|
|
|
2021-12-17 17:20:33 +08:00
|
|
|
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);
|
|
|
|
|
|
|
|
g_u32VSyncBlank++;
|
|
|
|
rt_completion_done(&vsync_wq);
|
|
|
|
}
|
|
|
|
else if (u32VpostIRQStatus & VPOSTB_UNDERRUN_INT)
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-12 19:15:17 +08:00
|
|
|
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);
|
|
|
|
|
2021-12-28 13:37:04 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* LCD clock is selected from UPLL and divide to 10MHz */
|
|
|
|
outpw(REG_CLK_DIVCTL1, (inpw(REG_CLK_DIVCTL1) & ~0xff1f) | 0xE19);
|
|
|
|
}
|
2021-05-12 19:15:17 +08:00
|
|
|
|
|
|
|
/* 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 = LCM_USING_BPP * 8;
|
|
|
|
psVpost->info.pixel_format = (LCM_USING_BPP == 4) ? RTGRAPHIC_PIXEL_FORMAT_ARGB888 : RTGRAPHIC_PIXEL_FORMAT_RGB565;
|
|
|
|
psVpost->info.pitch = psVpostLcmInst->u32DevWidth * LCM_USING_BPP;
|
|
|
|
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 (LCM_USING_BPP==4)
|
|
|
|
vpostSetVASrc(VA_SRC_RGB888);
|
|
|
|
#else
|
|
|
|
vpostSetVASrc(VA_SRC_RGB565);
|
|
|
|
#endif
|
2021-12-17 17:20:33 +08:00
|
|
|
psVpost->info.framebuffer = (rt_uint8_t *)vpostGetMultiFrameBuffer(DEF_VPOST_BUFFER_NUMBER);
|
2021-05-12 19:15:17 +08:00
|
|
|
}
|
|
|
|
#if defined(BSP_USING_VPOST_OSD)
|
|
|
|
else if (psVpost->layer == eVpost_OSD)
|
|
|
|
{
|
|
|
|
vpostOSDSetWindow(0, 0, psVpost->info.width, psVpost->info.height);
|
|
|
|
|
|
|
|
#if (LCM_USING_BPP==4)
|
|
|
|
vpostSetOSDSrc(OSD_SRC_RGB888);
|
|
|
|
#else
|
|
|
|
vpostSetOSDSrc(OSD_SRC_RGB565);
|
|
|
|
#endif
|
2021-12-17 17:20:33 +08:00
|
|
|
psVpost->info.framebuffer = (rt_uint8_t *)vpostGetMultiOSDBuffer(DEF_VPOST_BUFFER_NUMBER);
|
2021-05-12 19:15:17 +08:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (psVpost->info.framebuffer == NULL)
|
|
|
|
{
|
|
|
|
rt_kprintf("Fail to get VRAM buffer.\n");
|
|
|
|
RT_ASSERT(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
2021-12-17 17:20:33 +08:00
|
|
|
/* Register graphic device driver */
|
2021-05-12 19:15:17 +08:00
|
|
|
ret = rt_device_register(&psVpost->dev, psVpost->name, RT_DEVICE_FLAG_RDWR);
|
|
|
|
RT_ASSERT(ret == RT_EOK);
|
|
|
|
|
2021-12-17 17:20:33 +08:00
|
|
|
if (psVpost->layer == eVpost_LCD)
|
|
|
|
{
|
|
|
|
rt_hw_interrupt_install(psVpost->irqn, nu_vpost_isr, psVpost, psVpost->name);
|
|
|
|
rt_hw_interrupt_umask(psVpost->irqn);
|
|
|
|
}
|
|
|
|
|
2021-05-12 19:15:17 +08:00
|
|
|
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) */
|