diff --git a/arch/xtensa/src/esp32s3/esp32s3_cam.c b/arch/xtensa/src/esp32s3/esp32s3_cam.c index 01aa3b34429d3..7ce80a6624e38 100644 --- a/arch/xtensa/src/esp32s3/esp32s3_cam.c +++ b/arch/xtensa/src/esp32s3/esp32s3_cam.c @@ -75,6 +75,21 @@ #define ESP32S3_CAM_VSYNC_FILTER 4 +/* GDMA external memory block size setting for PSRAM RX. + * Change this single macro to switch between 16B / 32B / 64B. + * ESP32S3_CAM_DMA_ALIGN is the byte alignment derived from it. + */ + +#define ESP32S3_CAM_EXT_MEMBLK ESP32S3_DMA_EXT_MEMBLK_64B + +#if ESP32S3_CAM_EXT_MEMBLK == ESP32S3_DMA_EXT_MEMBLK_64B +# define ESP32S3_CAM_DMA_ALIGN 64 +#elif ESP32S3_CAM_EXT_MEMBLK == ESP32S3_DMA_EXT_MEMBLK_32B +# define ESP32S3_CAM_DMA_ALIGN 32 +#else +# define ESP32S3_CAM_DMA_ALIGN 16 +#endif + /**************************************************************************** * Private Types ****************************************************************************/ @@ -94,6 +109,7 @@ struct esp32s3_cam_s uint8_t *fb; /* Frame buffer */ uint32_t fb_size; /* Frame buffer size */ uint32_t fb_pos; /* Current write position */ + bool fb_allocated; /* true if driver allocated fb */ uint8_t vsync_cnt; /* VSYNC counter for frame sync */ imgdata_capture_t cb; /* Capture done callback */ @@ -127,6 +143,9 @@ static int esp32s3_cam_start_capture(struct imgdata_s *data, imgdata_capture_t callback, void *arg); static int esp32s3_cam_stop_capture(struct imgdata_s *data); +static void *esp32s3_cam_alloc(struct imgdata_s *data, + uint32_t align_size, uint32_t size); +static void esp32s3_cam_free(struct imgdata_s *data, void *addr); /**************************************************************************** * Private Data @@ -140,6 +159,8 @@ static const struct imgdata_ops_s g_cam_ops = .validate_frame_setting = esp32s3_cam_validate_frame_setting, .start_capture = esp32s3_cam_start_capture, .stop_capture = esp32s3_cam_stop_capture, + .alloc = esp32s3_cam_alloc, + .free = esp32s3_cam_free, }; static struct esp32s3_cam_s g_cam_priv = @@ -401,7 +422,7 @@ static int esp32s3_cam_dmasetup(struct esp32s3_cam_s *priv) esp32s3_dma_set_ext_memblk(priv->dma_channel, false, - ESP32S3_DMA_EXT_MEMBLK_64B); + ESP32S3_CAM_EXT_MEMBLK); return OK; } @@ -559,6 +580,7 @@ static int esp32s3_cam_uninit(struct imgdata_s *data) { struct esp32s3_cam_s *priv = (struct esp32s3_cam_s *)data; uint32_t regval; + irqstate_t flags; /* Stop capture */ @@ -566,20 +588,59 @@ static int esp32s3_cam_uninit(struct imgdata_s *data) regval &= ~LCD_CAM_CAM_START_M; putreg32(regval, LCD_CAM_CAM_CTRL1_REG); - /* Disable interrupt */ + /* Reset CAM module and AFIFO to stop all hardware activity */ - up_disable_irq(ESP32S3_IRQ_LCD_CAM); - irq_detach(ESP32S3_IRQ_LCD_CAM); - esp_teardown_irq(ESP32S3_IRQ_LCD_CAM, priv->cpuint); + regval = getreg32(LCD_CAM_CAM_CTRL1_REG); + regval |= LCD_CAM_CAM_RESET_M; + putreg32(regval, LCD_CAM_CAM_CTRL1_REG); + regval &= ~LCD_CAM_CAM_RESET_M; + putreg32(regval, LCD_CAM_CAM_CTRL1_REG); + + regval |= LCD_CAM_CAM_AFIFO_RESET_M; + putreg32(regval, LCD_CAM_CAM_CTRL1_REG); + regval &= ~LCD_CAM_CAM_AFIFO_RESET_M; + putreg32(regval, LCD_CAM_CAM_CTRL1_REG); + + /* Keep XCLK running so the sensor stays accessible via I2C for + * subsequent re-initialization. VSYNC generation is already + * stopped by the CAM_RESET above. Disable only the CAM_START + * bit in CAM_CTRL to stop the capture engine while preserving + * the clock divider configuration. + */ + + regval = getreg32(LCD_CAM_CAM_CTRL_REG); + regval &= ~LCD_CAM_CAM_UPDATE_REG_M; + putreg32(regval, LCD_CAM_CAM_CTRL_REG); - /* Release DMA */ + /* Stop and release DMA before tearing down the CPU-level IRQ. + * esp32s3_dma_release() detaches the GDMA channel interrupt; + * if DMA is still active it may fire after detach -> irq_unexpected. + */ if (priv->dma_channel >= 0) { + esp32s3_dma_reset_channel(priv->dma_channel, false); esp32s3_dma_release(priv->dma_channel); priv->dma_channel = -1; } + /* Mask CPU interrupts so no new peripheral interrupt can be + * delivered between clearing the pending flag and detaching + * the handler. XCLK is still running (kept for I2C access), + * so a VSYNC edge that arrived before the CAM_RESET could + * still be latched in the interrupt controller. + */ + + flags = spin_lock_irqsave(&priv->lock); + + putreg32(0, LCD_CAM_LC_DMA_INT_ENA_REG); + putreg32(0xffffffff, LCD_CAM_LC_DMA_INT_CLR_REG); + + up_disable_irq(ESP32S3_IRQ_LCD_CAM); + esp_teardown_irq(ESP32S3_PERIPH_LCD_CAM, priv->cpuint); + + spin_unlock_irqrestore(&priv->lock, flags); + /* Free DMA descriptors */ if (priv->dmadesc) @@ -588,14 +649,16 @@ static int esp32s3_cam_uninit(struct imgdata_s *data) priv->dmadesc = NULL; } - /* Free frame buffer */ + /* Free frame buffer only if driver allocated it */ - if (priv->fb) + if (priv->fb && priv->fb_allocated) { kmm_free(priv->fb); - priv->fb = NULL; } + priv->fb = NULL; + priv->fb_allocated = false; + priv->capturing = false; return OK; @@ -622,6 +685,7 @@ static int esp32s3_cam_set_buf(struct imgdata_s *data, { priv->fb = addr; priv->fb_size = size; + priv->fb_allocated = false; } else { @@ -630,12 +694,14 @@ static int esp32s3_cam_set_buf(struct imgdata_s *data, */ priv->fb_size = priv->width * priv->height * 2; - priv->fb = kmm_memalign(64, priv->fb_size); + priv->fb = kmm_memalign(ESP32S3_CAM_DMA_ALIGN, priv->fb_size); if (!priv->fb) { snerr("ERROR: Failed to allocate frame buffer\n"); return -ENOMEM; } + + priv->fb_allocated = true; } memset(priv->fb, 0, priv->fb_size); @@ -652,6 +718,31 @@ static int esp32s3_cam_set_buf(struct imgdata_s *data, return OK; } +/**************************************************************************** + * Name: esp32s3_cam_alloc + * + * Description: + * Allocate frame buffer memory with GDMA-required alignment. + * GDMA with EXT_MEMBLK_64B needs 64-byte aligned addresses + * for PSRAM access. + * + ****************************************************************************/ + +static void *esp32s3_cam_alloc(struct imgdata_s *data, + uint32_t align_size, uint32_t size) +{ + return kmm_memalign(ESP32S3_CAM_DMA_ALIGN, size); +} + +/**************************************************************************** + * Name: esp32s3_cam_free + ****************************************************************************/ + +static void esp32s3_cam_free(struct imgdata_s *data, void *addr) +{ + kmm_free(addr); +} + /**************************************************************************** * Name: esp32s3_cam_validate_frame_setting ****************************************************************************/ @@ -768,20 +859,48 @@ static int esp32s3_cam_stop_capture(struct imgdata_s *data) { struct esp32s3_cam_s *priv = (struct esp32s3_cam_s *)data; uint32_t regval; + irqstate_t flags; - /* Stop capture */ + flags = spin_lock_irqsave(&priv->lock); + + /* Mark not capturing first so ISR won't process further VSYNCs */ + + priv->capturing = false; + priv->cb = NULL; + priv->cb_arg = NULL; + + /* Stop capture engine */ regval = getreg32(LCD_CAM_CAM_CTRL1_REG); regval &= ~LCD_CAM_CAM_START_M; putreg32(regval, LCD_CAM_CAM_CTRL1_REG); + /* Reset CAM + AFIFO to fully quiesce hardware */ + + regval |= LCD_CAM_CAM_RESET_M; + putreg32(regval, LCD_CAM_CAM_CTRL1_REG); + regval &= ~LCD_CAM_CAM_RESET_M; + putreg32(regval, LCD_CAM_CAM_CTRL1_REG); + + regval |= LCD_CAM_CAM_AFIFO_RESET_M; + putreg32(regval, LCD_CAM_CAM_CTRL1_REG); + regval &= ~LCD_CAM_CAM_AFIFO_RESET_M; + putreg32(regval, LCD_CAM_CAM_CTRL1_REG); + regval = getreg32(LCD_CAM_CAM_CTRL_REG); regval |= LCD_CAM_CAM_UPDATE_REG_M; putreg32(regval, LCD_CAM_CAM_CTRL_REG); - priv->capturing = false; - priv->cb = NULL; - priv->cb_arg = NULL; + /* Reset DMA channel to abort any in-flight transfer */ + + esp32s3_dma_reset_channel(priv->dma_channel, false); + + /* Clear any pending VSYNC interrupt */ + + putreg32(LCD_CAM_CAM_VSYNC_INT_CLR_M, + LCD_CAM_LC_DMA_INT_CLR_REG); + + spin_unlock_irqrestore(&priv->lock, flags); return OK; } diff --git a/boards/xtensa/esp32s3/lckfb-szpi-esp32s3/configs/camera/defconfig b/boards/xtensa/esp32s3/lckfb-szpi-esp32s3/configs/camera/defconfig index 8f4f32fbda5d2..8ea3535c7f275 100644 --- a/boards/xtensa/esp32s3/lckfb-szpi-esp32s3/configs/camera/defconfig +++ b/boards/xtensa/esp32s3/lckfb-szpi-esp32s3/configs/camera/defconfig @@ -85,7 +85,6 @@ CONFIG_IOEXPANDER_PCA9557=y CONFIG_LCD=y CONFIG_LCD_DEV=y CONFIG_LCD_FRAMEBUFFER=y -CONFIG_LCD_PORTRAIT=y CONFIG_LCD_ST7789=y CONFIG_LCD_ST7789_DATA_ENDIAN_LITTLE=y CONFIG_LCD_ST7789_FREQUENCY=40000000 diff --git a/drivers/video/gc0308.c b/drivers/video/gc0308.c index e6a98753c6def..71c8652961e4e 100644 --- a/drivers/video/gc0308.c +++ b/drivers/video/gc0308.c @@ -134,9 +134,9 @@ #define GC0308_NATIVE_WIDTH 640 #define GC0308_NATIVE_HEIGHT 480 -/* Mirror/flip */ +/* Mirror/flip: register 0x14 (CISCTL_MODE1) bit[0] = horizontal mirror */ -#define GC0308_REG_CISCTL_MODE1_MIRROR 0x14 +#define GC0308_REG_CISCTL_MODE1_HMIRROR_BIT (1 << 0) /**************************************************************************** * Private Types @@ -461,8 +461,8 @@ static const struct v4l2_fmtdesc g_gc0308_fmtdescs[] = .description = "YUV 4:2:2 (YUYV)", }, { - .pixelformat = V4L2_PIX_FMT_RGB565, - .description = "RGB565", + .pixelformat = V4L2_PIX_FMT_RGB565X, + .description = "RGB565X (BE)", }, }; @@ -653,7 +653,10 @@ static int gc0308_init(struct imgsensor_s *sensor) up_mdelay(80); - /* Set default RGB565 output format: reg 0x24 bits[3:0] = 6 */ + /* Set RGB565 output: reg 0x24 bits[3:0] = 0x06 per datasheet. + * On 8-bit DVP bus the high byte is clocked out first, so the + * resulting memory layout is big-endian (RGB565X). + */ ret = gc0308_putreg(priv->i2c, GC0308_REG_RESET, 0x00); if (ret < 0) @@ -662,7 +665,7 @@ static int gc0308_init(struct imgsensor_s *sensor) } ret = gc0308_modreg(priv->i2c, GC0308_REG_OUTPUT_FMT, 0x0f, 0x06); - priv->pixelformat = IMGSENSOR_PIX_FMT_RGB565; + priv->pixelformat = IMGSENSOR_PIX_FMT_RGB565X; if (ret < 0) { return ret; @@ -760,7 +763,7 @@ static int gc0308_validate_frame_setting(struct imgsensor_s *sensor, if (datafmts[IMGSENSOR_FMT_MAIN].pixelformat != IMGSENSOR_PIX_FMT_YUYV && datafmts[IMGSENSOR_FMT_MAIN].pixelformat != - IMGSENSOR_PIX_FMT_RGB565) + IMGSENSOR_PIX_FMT_RGB565X) { return -EINVAL; } @@ -795,9 +798,9 @@ static int gc0308_start_capture(struct imgsensor_s *sensor, /* Configure output format register based on requested pixel format */ - if (datafmts[IMGSENSOR_FMT_MAIN].pixelformat == IMGSENSOR_PIX_FMT_RGB565) + if (datafmts[IMGSENSOR_FMT_MAIN].pixelformat == IMGSENSOR_PIX_FMT_RGB565X) { - fmtval = 0x06; /* RGB565 */ + fmtval = 0x06; /* Reg 0x24 = RGB565; BE in memory due to 8-bit DVP */ } else { @@ -836,7 +839,20 @@ static int gc0308_get_supported_value(struct imgsensor_s *sensor, uint32_t id, imgsensor_supported_value_t *value) { - return -ENOTTY; + switch (id) + { + case IMGSENSOR_ID_HFLIP_VIDEO: + case IMGSENSOR_ID_HFLIP_STILL: + value->type = IMGSENSOR_CTRL_TYPE_BOOLEAN; + value->u.range.minimum = 0; + value->u.range.maximum = 1; + value->u.range.step = 1; + value->u.range.default_value = 0; + return OK; + + default: + return -ENOTTY; + } } /**************************************************************************** @@ -847,7 +863,27 @@ static int gc0308_get_value(struct imgsensor_s *sensor, uint32_t id, uint32_t size, imgsensor_value_t *value) { - return -ENOTTY; + struct gc0308_dev_s *priv = (struct gc0308_dev_s *)sensor; + uint8_t regval; + int ret; + + switch (id) + { + case IMGSENSOR_ID_HFLIP_VIDEO: + case IMGSENSOR_ID_HFLIP_STILL: + ret = gc0308_getreg(priv->i2c, GC0308_REG_CISCTL_MODE1, ®val); + if (ret < 0) + { + return ret; + } + + value->value32 = + (regval & GC0308_REG_CISCTL_MODE1_HMIRROR_BIT) ? 1 : 0; + return OK; + + default: + return -ENOTTY; + } } /**************************************************************************** @@ -858,7 +894,20 @@ static int gc0308_set_value(struct imgsensor_s *sensor, uint32_t id, uint32_t size, imgsensor_value_t value) { - return -ENOTTY; + struct gc0308_dev_s *priv = (struct gc0308_dev_s *)sensor; + + switch (id) + { + case IMGSENSOR_ID_HFLIP_VIDEO: + case IMGSENSOR_ID_HFLIP_STILL: + return gc0308_modreg(priv->i2c, GC0308_REG_CISCTL_MODE1, + GC0308_REG_CISCTL_MODE1_HMIRROR_BIT, + value.value32 ? + GC0308_REG_CISCTL_MODE1_HMIRROR_BIT : 0); + + default: + return -ENOTTY; + } } /****************************************************************************