Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 133 additions & 14 deletions arch/xtensa/src/esp32s3/esp32s3_cam.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
****************************************************************************/
Expand All @@ -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 */
Expand Down Expand Up @@ -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
Expand All @@ -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 =
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -559,27 +580,67 @@ 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 */

regval = getreg32(LCD_CAM_CAM_CTRL1_REG);
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)
Expand All @@ -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;
Expand All @@ -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
{
Expand All @@ -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);
Expand All @@ -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
****************************************************************************/
Expand Down Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading
Loading