From 4bd54f62a76181744b0e0300d1c51ef6da208305 Mon Sep 17 00:00:00 2001 From: liamHowatt Date: Mon, 18 Nov 2024 04:28:38 +0000 Subject: [PATCH] arch/esp32s3: fb add pandisplay Signed-off-by: liamHowatt --- arch/xtensa/src/esp32s3/Kconfig | 7 ++ arch/xtensa/src/esp32s3/esp32s3_lcd.c | 129 ++++++++++++++++++++++++-- drivers/video/fb.c | 6 +- 3 files changed, 133 insertions(+), 9 deletions(-) diff --git a/arch/xtensa/src/esp32s3/Kconfig b/arch/xtensa/src/esp32s3/Kconfig index 44b7913436b6a..009f31e93ba79 100644 --- a/arch/xtensa/src/esp32s3/Kconfig +++ b/arch/xtensa/src/esp32s3/Kconfig @@ -2534,6 +2534,13 @@ config ESP32S3_LCD_BUFFER_LAYERS int "LCD Buffer Layer Number" default 1 +config ESP32S3_LCD_DOUBLE_BUFFERED + bool "LCD Double Buffered" + default y + ---help--- + Double the framebuffer size per layer. + Twice as much memory will be allocated. + choice prompt "LCD Data Width" default ESP32S3_LCD_DATA_16BIT diff --git a/arch/xtensa/src/esp32s3/esp32s3_lcd.c b/arch/xtensa/src/esp32s3/esp32s3_lcd.c index b0009e4ec74b3..258a2a895cac4 100644 --- a/arch/xtensa/src/esp32s3/esp32s3_lcd.c +++ b/arch/xtensa/src/esp32s3/esp32s3_lcd.c @@ -135,6 +135,18 @@ CONFIG_ESP32S3_LCD_VRES * \ ESP32S3_LCD_DATA_WIDTH) +#ifdef CONFIG_ESP32S3_LCD_DOUBLE_BUFFERED +# define ESP32S3_LCD_FB_MULT 2 +#else +# define ESP32S3_LCD_FB_MULT 1 +#endif + +#define ESP32S3_LCD_HRES_VIRTUAL CONFIG_ESP32S3_LCD_HRES +#define ESP32S3_LCD_VRES_VIRTUAL (CONFIG_ESP32S3_LCD_VRES * \ + ESP32S3_LCD_FB_MULT) + +#define ESP32S3_LCD_FB_MEM_SIZE (ESP32S3_LCD_FB_SIZE * ESP32S3_LCD_FB_MULT) + #define ESP32S3_LCD_DMADESC_NUM (ESP32S3_LCD_FB_SIZE / \ ESP32S3_DMA_BUFLEN_MAX + 1) @@ -188,6 +200,8 @@ struct esp32s3_lcd_s uint8_t cur_layer; /* Current layer number */ + uint32_t yoffset; /* The current pan offset */ + int cpuint; /* CPU interrupt assigned to this LCD */ uint8_t cpu; /* CPU ID */ int32_t dma_channel; /* DMA channel */ @@ -244,6 +258,11 @@ static int esp32s3_lcd_base_updatearea(struct fb_vtable_s *vtable, const struct fb_area_s *area); #endif +#ifdef CONFIG_ESP32S3_LCD_DOUBLE_BUFFERED +static int esp32s3_lcd_base_pandisplay(struct fb_vtable_s *vtable, + struct fb_planeinfo_s *pinfo); +#endif + /* Initialization ***********************************************************/ static int esp32s3_lcd_dmasetup(void); @@ -300,13 +319,16 @@ static const struct fb_videoinfo_s g_base_videoinfo = /* This structure provides the base layer interface */ -static const struct fb_vtable_s g_base_vtable = +static struct fb_vtable_s g_base_vtable = { .getvideoinfo = esp32s3_lcd_base_getvideoinfo, .getplaneinfo = esp32s3_lcd_base_getplaneinfo, #ifdef CONFIG_FB_UPDATE .updatearea = esp32s3_lcd_base_updatearea, #endif +#ifdef CONFIG_ESP32S3_LCD_DOUBLE_BUFFERED + .pandisplay = esp32s3_lcd_base_pandisplay, +#endif }; /**************************************************************************** @@ -515,9 +537,11 @@ static int esp32s3_lcd_base_getplaneinfo(struct fb_vtable_s *vtable, pinfo->display = 0; pinfo->fbmem = (void *)layer->framebuffer; - pinfo->fblen = ESP32S3_LCD_FB_SIZE; + pinfo->fblen = ESP32S3_LCD_FB_MEM_SIZE; pinfo->stride = ESP32S3_LCD_STRIDE; pinfo->bpp = ESP32S3_LCD_DATA_BPP; + pinfo->xres_virtual = ESP32S3_LCD_HRES_VIRTUAL; + pinfo->yres_virtual = ESP32S3_LCD_VRES_VIRTUAL; return OK; } @@ -545,10 +569,66 @@ static int esp32s3_lcd_base_getplaneinfo(struct fb_vtable_s *vtable, static int esp32s3_lcd_base_updatearea(struct fb_vtable_s *vtable, const struct fb_area_s *area) { + if (area->w == 0 || area->h == 0) + { + return 0; + } + + if (area->x + area->w > ESP32S3_LCD_HRES_VIRTUAL || + area->y + area->h > ESP32S3_LCD_VRES_VIRTUAL) + { + gerr("ERROR: updatearea area is out of bounds. " + "x: %" PRIu16 ", y: %" PRIu16 ", w: %" PRIu16 ", h: %" PRIu16 ", " + "virtual hres: %d, virtual vres: %d\n", + area->x, area->y, area->w, area->h, + ESP32S3_LCD_HRES_VIRTUAL, ESP32S3_LCD_VRES_VIRTUAL); + return -EINVAL; + } + struct esp32s3_lcd_s *priv = &g_lcd_priv; - cache_writeback_addr(CURRENT_LAYER(priv)->framebuffer, - ESP32S3_LCD_FB_SIZE); + uint8_t *first_pixel = CURRENT_LAYER(priv)->framebuffer + + (area->y * ESP32S3_LCD_STRIDE + + area->x * ESP32S3_LCD_DATA_WIDTH); + + uint32_t size = (area->h - 1) * ESP32S3_LCD_STRIDE + + area->w * ESP32S3_LCD_DATA_WIDTH; + + cache_writeback_addr(first_pixel, size); + + return 0; +} +#endif + +/**************************************************************************** + * Name: esp32s3_lcd_base_pandisplay + * + * Description: + * Validate the pan info. The pan info is queued by the framebuffer + * subsystem. + * + * Input Parameters: + * vtable - The framebuffer driver object + * pinfo - the planeinfo object + * + * Returned Value: + * Zero is returned on success; a negated errno value is returned on any + * failure. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP32S3_LCD_DOUBLE_BUFFERED +static int esp32s3_lcd_base_pandisplay(struct fb_vtable_s *vtable, + struct fb_planeinfo_s *pinfo) +{ + if (pinfo->yoffset > ESP32S3_LCD_VRES_VIRTUAL - CONFIG_ESP32S3_LCD_VRES) + { + gerr("ERROR: pandisplay yoffset out of bounds: %" PRIu32 ". " + "The maximum is: %d\n", + pinfo->yoffset, + ESP32S3_LCD_VRES_VIRTUAL - CONFIG_ESP32S3_LCD_VRES); + return -EINVAL; + } return 0; } @@ -575,6 +655,8 @@ static int IRAM_ATTR lcd_interrupt(int irq, void *context, void *arg) uint32_t regval; struct esp32s3_lcd_s *priv = &g_lcd_priv; uint32_t status = esp32s3_lcd_getreg(LCD_CAM_LC_DMA_INT_ST_REG); + union fb_paninfo_u info; + struct esp32s3_layer_s *layer; esp32s3_lcd_putreg(LCD_CAM_LC_DMA_INT_CLR_REG, status); if (status & LCD_CAM_LCD_VSYNC_INT_ST_M) @@ -603,10 +685,41 @@ static int IRAM_ATTR lcd_interrupt(int irq, void *context, void *arg) true); #endif +#ifdef CONFIG_ESP32S3_LCD_DOUBLE_BUFFERED + /* Pan the display to a new buffer offset if one was queued */ + + if (fb_paninfo_count(&g_base_vtable, FB_NO_OVERLAY) > 1) + { + fb_remove_paninfo(&g_base_vtable, FB_NO_OVERLAY); + } + + if (fb_peek_paninfo(&g_base_vtable, &info, FB_NO_OVERLAY) == OK) + { + priv->yoffset = info.planeinfo.yoffset; + layer = CURRENT_LAYER(priv); + + esp32s3_dma_setup(layer->dmadesc, + ESP32S3_LCD_DMADESC_NUM, + &layer->framebuffer[priv->yoffset * + ESP32S3_LCD_STRIDE], + ESP32S3_LCD_FB_SIZE, + true, + priv->dma_channel); + + /* Leave this paninfo in the panbuffer so the buffer will be full + * after the user adds another. poll will report unreadyness to + * write until it's taken by the next cycle here. + */ + } +#endif + #ifndef CONFIG_FB_UPDATE /* Write framebuffer data from D-cache to PSRAM */ - cache_writeback_addr(CURRENT_LAYER(priv)->framebuffer, + layer = CURRENT_LAYER(priv); + + cache_writeback_addr(&layer->framebuffer[priv->yoffset * + ESP32S3_LCD_STRIDE], ESP32S3_LCD_FB_SIZE); #endif @@ -662,9 +775,9 @@ static int esp32s3_lcd_dmasetup(void) { struct esp32s3_layer_s *layer = &priv->layer[i]; - layer->framebuffer = memalign(64, ESP32S3_LCD_FB_SIZE); + layer->framebuffer = memalign(64, ESP32S3_LCD_FB_MEM_SIZE); DEBUGASSERT(layer->framebuffer != NULL); - memset(layer->framebuffer, 0, ESP32S3_LCD_FB_SIZE); + memset(layer->framebuffer, 0, ESP32S3_LCD_FB_MEM_SIZE); esp32s3_dma_setup(layer->dmadesc, ESP32S3_LCD_DMADESC_NUM, @@ -1013,7 +1126,7 @@ struct fb_vtable_s *up_fbgetvplane(int display, int vplane) lcdinfo("vplane: %d\n", vplane); if (vplane == 0) { - return (struct fb_vtable_s *)&g_base_vtable; + return &g_base_vtable; } else { diff --git a/drivers/video/fb.c b/drivers/video/fb.c index 49ccdae76bc54..4b0f5757129f5 100644 --- a/drivers/video/fb.c +++ b/drivers/video/fb.c @@ -987,7 +987,11 @@ static int fb_ioctl(FAR struct file *filep, int cmd, unsigned long arg) if (fb->vtable->pandisplay != NULL) { - fb->vtable->pandisplay(fb->vtable, pinfo); + ret = fb->vtable->pandisplay(fb->vtable, pinfo); + if (ret < 0) + { + break; + } } ret = fb_add_paninfo(fb, &paninfo, FB_NO_OVERLAY);