mirror of
https://github.com/apache/nuttx.git
synced 2026-05-30 05:16:47 +08:00
xtensa/esp32s3: fix esp32s3_cam uninit/stop_capture bugs
Fix several issues in the ESP32-S3 CAM driver: - stop_capture: reset DMA channel, CAM module and AFIFO under spinlock to fully quiesce hardware before returning. Clear pending VSYNC interrupt to prevent stale ISR firing. - uninit: reset CAM/AFIFO before releasing DMA to prevent in-flight transfers after channel detach. Use esp_teardown_irq with correct peripheral ID (ESP32S3_PERIPH_LCD_CAM) instead of irq_detach which corrupts the shared IRQ mapping table. Mask interrupts and clear pending flags under spinlock before detaching handler. - uninit: preserve XCLK output so the sensor remains accessible via I2C for subsequent re-initialization. - set_buf/uninit: track driver-allocated vs user-provided frame buffers with fb_allocated flag to prevent double-free when using V4L2 USERPTR mode. Signed-off-by: wangjianyu3 <wangjianyu3@xiaomi.com>
This commit is contained in:
@@ -94,6 +94,7 @@ struct esp32s3_cam_s
|
|||||||
uint8_t *fb; /* Frame buffer */
|
uint8_t *fb; /* Frame buffer */
|
||||||
uint32_t fb_size; /* Frame buffer size */
|
uint32_t fb_size; /* Frame buffer size */
|
||||||
uint32_t fb_pos; /* Current write position */
|
uint32_t fb_pos; /* Current write position */
|
||||||
|
bool fb_allocated; /* true if driver allocated fb */
|
||||||
uint8_t vsync_cnt; /* VSYNC counter for frame sync */
|
uint8_t vsync_cnt; /* VSYNC counter for frame sync */
|
||||||
|
|
||||||
imgdata_capture_t cb; /* Capture done callback */
|
imgdata_capture_t cb; /* Capture done callback */
|
||||||
@@ -559,6 +560,7 @@ static int esp32s3_cam_uninit(struct imgdata_s *data)
|
|||||||
{
|
{
|
||||||
struct esp32s3_cam_s *priv = (struct esp32s3_cam_s *)data;
|
struct esp32s3_cam_s *priv = (struct esp32s3_cam_s *)data;
|
||||||
uint32_t regval;
|
uint32_t regval;
|
||||||
|
irqstate_t flags;
|
||||||
|
|
||||||
/* Stop capture */
|
/* Stop capture */
|
||||||
|
|
||||||
@@ -566,20 +568,59 @@ static int esp32s3_cam_uninit(struct imgdata_s *data)
|
|||||||
regval &= ~LCD_CAM_CAM_START_M;
|
regval &= ~LCD_CAM_CAM_START_M;
|
||||||
putreg32(regval, LCD_CAM_CAM_CTRL1_REG);
|
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);
|
regval = getreg32(LCD_CAM_CAM_CTRL1_REG);
|
||||||
irq_detach(ESP32S3_IRQ_LCD_CAM);
|
regval |= LCD_CAM_CAM_RESET_M;
|
||||||
esp_teardown_irq(ESP32S3_IRQ_LCD_CAM, priv->cpuint);
|
putreg32(regval, LCD_CAM_CAM_CTRL1_REG);
|
||||||
|
regval &= ~LCD_CAM_CAM_RESET_M;
|
||||||
|
putreg32(regval, LCD_CAM_CAM_CTRL1_REG);
|
||||||
|
|
||||||
/* Release DMA */
|
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);
|
||||||
|
|
||||||
|
/* 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)
|
if (priv->dma_channel >= 0)
|
||||||
{
|
{
|
||||||
|
esp32s3_dma_reset_channel(priv->dma_channel, false);
|
||||||
esp32s3_dma_release(priv->dma_channel);
|
esp32s3_dma_release(priv->dma_channel);
|
||||||
priv->dma_channel = -1;
|
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 */
|
/* Free DMA descriptors */
|
||||||
|
|
||||||
if (priv->dmadesc)
|
if (priv->dmadesc)
|
||||||
@@ -588,14 +629,16 @@ static int esp32s3_cam_uninit(struct imgdata_s *data)
|
|||||||
priv->dmadesc = NULL;
|
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);
|
kmm_free(priv->fb);
|
||||||
priv->fb = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
priv->fb = NULL;
|
||||||
|
priv->fb_allocated = false;
|
||||||
|
|
||||||
priv->capturing = false;
|
priv->capturing = false;
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
@@ -622,6 +665,7 @@ static int esp32s3_cam_set_buf(struct imgdata_s *data,
|
|||||||
{
|
{
|
||||||
priv->fb = addr;
|
priv->fb = addr;
|
||||||
priv->fb_size = size;
|
priv->fb_size = size;
|
||||||
|
priv->fb_allocated = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -636,6 +680,8 @@ static int esp32s3_cam_set_buf(struct imgdata_s *data,
|
|||||||
snerr("ERROR: Failed to allocate frame buffer\n");
|
snerr("ERROR: Failed to allocate frame buffer\n");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
priv->fb_allocated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(priv->fb, 0, priv->fb_size);
|
memset(priv->fb, 0, priv->fb_size);
|
||||||
@@ -768,20 +814,48 @@ static int esp32s3_cam_stop_capture(struct imgdata_s *data)
|
|||||||
{
|
{
|
||||||
struct esp32s3_cam_s *priv = (struct esp32s3_cam_s *)data;
|
struct esp32s3_cam_s *priv = (struct esp32s3_cam_s *)data;
|
||||||
uint32_t regval;
|
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 = getreg32(LCD_CAM_CAM_CTRL1_REG);
|
||||||
regval &= ~LCD_CAM_CAM_START_M;
|
regval &= ~LCD_CAM_CAM_START_M;
|
||||||
putreg32(regval, LCD_CAM_CAM_CTRL1_REG);
|
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 = getreg32(LCD_CAM_CAM_CTRL_REG);
|
||||||
regval |= LCD_CAM_CAM_UPDATE_REG_M;
|
regval |= LCD_CAM_CAM_UPDATE_REG_M;
|
||||||
putreg32(regval, LCD_CAM_CAM_CTRL_REG);
|
putreg32(regval, LCD_CAM_CAM_CTRL_REG);
|
||||||
|
|
||||||
priv->capturing = false;
|
/* Reset DMA channel to abort any in-flight transfer */
|
||||||
priv->cb = NULL;
|
|
||||||
priv->cb_arg = NULL;
|
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;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user