drivers: video: Add support for STM32 DCMI
Add Kconfig, DCMI driver, Yaml, and CMakeLists files Signed-off-by: Charles Dias <charlesdias.cd@outlook.com>
This commit is contained in:
parent
d49ba8b95e
commit
bdd4e61a25
5 changed files with 652 additions and 0 deletions
|
@ -9,3 +9,4 @@ zephyr_library_sources_ifdef(CONFIG_VIDEO_SW_GENERATOR video_sw_generator.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_VIDEO_MT9M114 mt9m114.c)
|
zephyr_library_sources_ifdef(CONFIG_VIDEO_MT9M114 mt9m114.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_VIDEO_OV7725 ov7725.c)
|
zephyr_library_sources_ifdef(CONFIG_VIDEO_OV7725 ov7725.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_VIDEO_OV2640 ov2640.c)
|
zephyr_library_sources_ifdef(CONFIG_VIDEO_OV2640 ov2640.c)
|
||||||
|
zephyr_library_sources_ifdef(CONFIG_VIDEO_STM32_DCMI video_stm32_dcmi.c)
|
||||||
|
|
|
@ -41,4 +41,6 @@ source "drivers/video/Kconfig.ov7725"
|
||||||
|
|
||||||
source "drivers/video/Kconfig.ov2640"
|
source "drivers/video/Kconfig.ov2640"
|
||||||
|
|
||||||
|
source "drivers/video/Kconfig.stm32_dcmi"
|
||||||
|
|
||||||
endif # VIDEO
|
endif # VIDEO
|
||||||
|
|
22
drivers/video/Kconfig.stm32_dcmi
Normal file
22
drivers/video/Kconfig.stm32_dcmi
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
# STM32 DCMI driver configuration options
|
||||||
|
|
||||||
|
# Copyright (c) 2024 Charles Dias <charlesdias.cd@outlook.com>
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
DT_STM32_DCMI_HAS_DMA := $(dt_nodelabel_has_prop,dcmi,dmas)
|
||||||
|
|
||||||
|
config VIDEO_STM32_DCMI
|
||||||
|
bool "STM32 Digital camera interface (DCMI) driver"
|
||||||
|
default y
|
||||||
|
depends on DT_HAS_ST_STM32_DCMI_ENABLED
|
||||||
|
select USE_STM32_HAL_DCMI
|
||||||
|
select USE_STM32_HAL_MDMA if SOC_SERIES_STM32H7X
|
||||||
|
select DMA if $(DT_STM32_DCMI_HAS_DMA)
|
||||||
|
select USE_STM32_HAL_DMA if $(DT_STM32_DCMI_HAS_DMA)
|
||||||
|
select USE_STM32_HAL_DMA_EX if $(DT_STM32_DCMI_HAS_DMA)
|
||||||
|
help
|
||||||
|
Enable driver for STM32 Digital camera interface periheral.
|
||||||
|
|
||||||
|
module = STM32_DCMI
|
||||||
|
module-str = stm32_dcmi
|
||||||
|
source "subsys/logging/Kconfig.template.log_config"
|
504
drivers/video/video_stm32_dcmi.c
Normal file
504
drivers/video/video_stm32_dcmi.c
Normal file
|
@ -0,0 +1,504 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Charles Dias <charlesdias.cd@outlook.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define DT_DRV_COMPAT st_stm32_dcmi
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/drivers/video.h>
|
||||||
|
#include <zephyr/drivers/pinctrl.h>
|
||||||
|
#include <zephyr/irq.h>
|
||||||
|
#include <zephyr/drivers/clock_control/stm32_clock_control.h>
|
||||||
|
#include <zephyr/drivers/clock_control.h>
|
||||||
|
#include <zephyr/drivers/dma.h>
|
||||||
|
#include <zephyr/drivers/dma/dma_stm32.h>
|
||||||
|
|
||||||
|
#include <stm32_ll_dma.h>
|
||||||
|
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
LOG_MODULE_REGISTER(video_stm32_dcmi, CONFIG_STM32_DCMI_LOG_LEVEL);
|
||||||
|
|
||||||
|
K_HEAP_DEFINE(video_stm32_buffer_pool, CONFIG_VIDEO_BUFFER_POOL_SZ_MAX);
|
||||||
|
|
||||||
|
typedef void (*irq_config_func_t)(const struct device *dev);
|
||||||
|
|
||||||
|
struct stream {
|
||||||
|
DMA_TypeDef *reg;
|
||||||
|
const struct device *dma_dev;
|
||||||
|
uint32_t channel;
|
||||||
|
struct dma_config cfg;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct video_stm32_dcmi_data {
|
||||||
|
const struct device *dev;
|
||||||
|
DCMI_HandleTypeDef hdcmi;
|
||||||
|
struct video_format fmt;
|
||||||
|
struct k_fifo fifo_in;
|
||||||
|
struct k_fifo fifo_out;
|
||||||
|
uint32_t pixel_format;
|
||||||
|
uint32_t height;
|
||||||
|
uint32_t width;
|
||||||
|
uint32_t pitch;
|
||||||
|
uint8_t *buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct video_stm32_dcmi_config {
|
||||||
|
struct stm32_pclken pclken;
|
||||||
|
irq_config_func_t irq_config;
|
||||||
|
const struct pinctrl_dev_config *pctrl;
|
||||||
|
const struct device *sensor_dev;
|
||||||
|
const struct stream dma;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline unsigned int video_pix_fmt_bpp(uint32_t pixelformat)
|
||||||
|
{
|
||||||
|
switch (pixelformat) {
|
||||||
|
case VIDEO_PIX_FMT_BGGR8:
|
||||||
|
case VIDEO_PIX_FMT_GBRG8:
|
||||||
|
case VIDEO_PIX_FMT_GRBG8:
|
||||||
|
case VIDEO_PIX_FMT_RGGB8:
|
||||||
|
return 1;
|
||||||
|
case VIDEO_PIX_FMT_RGB565:
|
||||||
|
case VIDEO_PIX_FMT_YUYV:
|
||||||
|
return 2;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HAL_DCMI_ErrorCallback(DCMI_HandleTypeDef *hdcmi)
|
||||||
|
{
|
||||||
|
LOG_WRN("%s", __func__);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HAL_DCMI_FrameEventCallback(DCMI_HandleTypeDef *hdcmi)
|
||||||
|
{
|
||||||
|
struct video_stm32_dcmi_data *dev_data =
|
||||||
|
CONTAINER_OF(hdcmi, struct video_stm32_dcmi_data, hdcmi);
|
||||||
|
struct video_buffer *vbuf;
|
||||||
|
|
||||||
|
HAL_DCMI_Suspend(hdcmi);
|
||||||
|
|
||||||
|
vbuf = k_fifo_get(&dev_data->fifo_in, K_NO_WAIT);
|
||||||
|
|
||||||
|
if (vbuf == NULL) {
|
||||||
|
LOG_DBG("Failed to get buffer from fifo");
|
||||||
|
goto resume;
|
||||||
|
}
|
||||||
|
|
||||||
|
vbuf->timestamp = k_uptime_get_32();
|
||||||
|
memcpy(vbuf->buffer, dev_data->buffer, vbuf->bytesused);
|
||||||
|
|
||||||
|
k_fifo_put(&dev_data->fifo_out, vbuf);
|
||||||
|
|
||||||
|
resume:
|
||||||
|
HAL_DCMI_Resume(hdcmi);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stm32_dcmi_isr(const struct device *dev)
|
||||||
|
{
|
||||||
|
struct video_stm32_dcmi_data *data = dev->data;
|
||||||
|
|
||||||
|
HAL_DCMI_IRQHandler(&data->hdcmi);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dmci_dma_callback(const struct device *dev, void *arg,
|
||||||
|
uint32_t channel, int status)
|
||||||
|
{
|
||||||
|
DMA_HandleTypeDef *hdma = arg;
|
||||||
|
|
||||||
|
ARG_UNUSED(dev);
|
||||||
|
|
||||||
|
if (status < 0) {
|
||||||
|
LOG_ERR("DMA callback error with channel %d.", channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
HAL_DMA_IRQHandler(hdma);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HAL_DMA_ErrorCallback(DMA_HandleTypeDef *hdma)
|
||||||
|
{
|
||||||
|
LOG_WRN("%s", __func__);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stm32_dma_init(const struct device *dev)
|
||||||
|
{
|
||||||
|
struct video_stm32_dcmi_data *data = dev->data;
|
||||||
|
const struct video_stm32_dcmi_config *config = dev->config;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Check if the DMA device is ready */
|
||||||
|
if (!device_is_ready(config->dma.dma_dev)) {
|
||||||
|
LOG_ERR("%s DMA device not ready", config->dma.dma_dev->name);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DMA configuration
|
||||||
|
* Due to use of QSPI HAL API in current driver,
|
||||||
|
* both HAL and Zephyr DMA drivers should be configured.
|
||||||
|
* The required configuration for Zephyr DMA driver should only provide
|
||||||
|
* the minimum information to inform the DMA slot will be in used and
|
||||||
|
* how to route callbacks.
|
||||||
|
*/
|
||||||
|
struct dma_config dma_cfg = config->dma.cfg;
|
||||||
|
static DMA_HandleTypeDef hdma;
|
||||||
|
|
||||||
|
/* Proceed to the minimum Zephyr DMA driver init */
|
||||||
|
dma_cfg.user_data = &hdma;
|
||||||
|
/* HACK: This field is used to inform driver that it is overridden */
|
||||||
|
dma_cfg.linked_channel = STM32_DMA_HAL_OVERRIDE;
|
||||||
|
/* Because of the STREAM OFFSET, the DMA channel given here is from 1 - 8 */
|
||||||
|
ret = dma_config(config->dma.dma_dev,
|
||||||
|
config->dma.channel + STM32_DMA_STREAM_OFFSET, &dma_cfg);
|
||||||
|
if (ret != 0) {
|
||||||
|
LOG_ERR("Failed to configure DMA channel %d",
|
||||||
|
config->dma.channel + STM32_DMA_STREAM_OFFSET);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Configure the DMA ***/
|
||||||
|
/* Set the parameters to be configured */
|
||||||
|
hdma.Init.Request = DMA_REQUEST_DCMI;
|
||||||
|
hdma.Init.Direction = DMA_PERIPH_TO_MEMORY;
|
||||||
|
hdma.Init.PeriphInc = DMA_PINC_DISABLE;
|
||||||
|
hdma.Init.MemInc = DMA_MINC_ENABLE;
|
||||||
|
hdma.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
|
||||||
|
hdma.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
|
||||||
|
hdma.Init.Mode = DMA_CIRCULAR;
|
||||||
|
hdma.Init.Priority = DMA_PRIORITY_HIGH;
|
||||||
|
hdma.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
|
||||||
|
|
||||||
|
hdma.Instance = __LL_DMA_GET_STREAM_INSTANCE(config->dma.reg,
|
||||||
|
config->dma.channel);
|
||||||
|
|
||||||
|
/* Initialize DMA HAL */
|
||||||
|
__HAL_LINKDMA(&data->hdcmi, DMA_Handle, hdma);
|
||||||
|
|
||||||
|
if (HAL_DMA_Init(&hdma) != HAL_OK) {
|
||||||
|
LOG_ERR("DCMI DMA Init failed");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stm32_dcmi_enable_clock(const struct device *dev)
|
||||||
|
{
|
||||||
|
const struct video_stm32_dcmi_config *config = dev->config;
|
||||||
|
const struct device *dcmi_clock = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE);
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (!device_is_ready(dcmi_clock)) {
|
||||||
|
LOG_ERR("clock control device not ready");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Turn on DCMI peripheral clock */
|
||||||
|
err = clock_control_on(dcmi_clock, (clock_control_subsys_t *) &config->pclken);
|
||||||
|
if (err < 0) {
|
||||||
|
LOG_ERR("Failed to enable DCMI clock. Error %d", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int video_stm32_dcmi_set_fmt(const struct device *dev,
|
||||||
|
enum video_endpoint_id ep,
|
||||||
|
struct video_format *fmt)
|
||||||
|
{
|
||||||
|
const struct video_stm32_dcmi_config *config = dev->config;
|
||||||
|
struct video_stm32_dcmi_data *data = dev->data;
|
||||||
|
unsigned int bpp = video_pix_fmt_bpp(fmt->pixelformat);
|
||||||
|
|
||||||
|
if (!bpp || ep != VIDEO_EP_OUT) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->pixel_format = fmt->pixelformat;
|
||||||
|
data->pitch = fmt->pitch;
|
||||||
|
data->height = fmt->height;
|
||||||
|
data->width = fmt->width;
|
||||||
|
|
||||||
|
if (video_set_format(config->sensor_dev, ep, fmt)) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int video_stm32_dcmi_get_fmt(const struct device *dev,
|
||||||
|
enum video_endpoint_id ep,
|
||||||
|
struct video_format *fmt)
|
||||||
|
{
|
||||||
|
struct video_stm32_dcmi_data *data = dev->data;
|
||||||
|
const struct video_stm32_dcmi_config *config = dev->config;
|
||||||
|
|
||||||
|
if ((fmt == NULL) || (ep != VIDEO_EP_OUT)) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!video_get_format(config->sensor_dev, ep, fmt)) {
|
||||||
|
/* align DCMI with sensor fmt */
|
||||||
|
return video_stm32_dcmi_set_fmt(dev, ep, fmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt->pixelformat = data->pixel_format;
|
||||||
|
fmt->height = data->height;
|
||||||
|
fmt->width = data->width;
|
||||||
|
fmt->pitch = data->pitch;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int video_stm32_dcmi_stream_start(const struct device *dev)
|
||||||
|
{
|
||||||
|
struct video_stm32_dcmi_data *data = dev->data;
|
||||||
|
const struct video_stm32_dcmi_config *config = dev->config;
|
||||||
|
size_t buffer_size = data->pitch * data->height;
|
||||||
|
|
||||||
|
data->buffer = k_heap_alloc(&video_stm32_buffer_pool, buffer_size, K_NO_WAIT);
|
||||||
|
if (data->buffer == NULL) {
|
||||||
|
LOG_ERR("Failed to allocate DCMI buffer for image. Size %d bytes", buffer_size);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
int err = HAL_DCMI_Start_DMA(&data->hdcmi, DCMI_MODE_CONTINUOUS,
|
||||||
|
(uint32_t)data->buffer, buffer_size / 4);
|
||||||
|
if (err != HAL_OK) {
|
||||||
|
LOG_ERR("Failed to start DCMI DMA");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (video_stream_start(config->sensor_dev)) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int video_stm32_dcmi_stream_stop(const struct device *dev)
|
||||||
|
{
|
||||||
|
struct video_stm32_dcmi_data *data = dev->data;
|
||||||
|
const struct video_stm32_dcmi_config *config = dev->config;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (video_stream_stop(config->sensor_dev)) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Release the buffer allocated in stream_start */
|
||||||
|
k_heap_free(&video_stm32_buffer_pool, data->buffer);
|
||||||
|
|
||||||
|
err = HAL_DCMI_Stop(&data->hdcmi);
|
||||||
|
if (err != HAL_OK) {
|
||||||
|
LOG_ERR("Failed to stop DCMI");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int video_stm32_dcmi_enqueue(const struct device *dev,
|
||||||
|
enum video_endpoint_id ep,
|
||||||
|
struct video_buffer *vbuf)
|
||||||
|
{
|
||||||
|
struct video_stm32_dcmi_data *data = dev->data;
|
||||||
|
|
||||||
|
if (ep != VIDEO_EP_OUT) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
vbuf->bytesused = data->pitch * data->height;
|
||||||
|
|
||||||
|
k_fifo_put(&data->fifo_in, vbuf);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int video_stm32_dcmi_dequeue(const struct device *dev,
|
||||||
|
enum video_endpoint_id ep,
|
||||||
|
struct video_buffer **vbuf,
|
||||||
|
k_timeout_t timeout)
|
||||||
|
{
|
||||||
|
struct video_stm32_dcmi_data *data = dev->data;
|
||||||
|
|
||||||
|
if (ep != VIDEO_EP_OUT) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*vbuf = k_fifo_get(&data->fifo_out, timeout);
|
||||||
|
if (*vbuf == NULL) {
|
||||||
|
return -EAGAIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int video_stm32_dcmi_get_caps(const struct device *dev,
|
||||||
|
enum video_endpoint_id ep,
|
||||||
|
struct video_caps *caps)
|
||||||
|
{
|
||||||
|
const struct video_stm32_dcmi_config *config = dev->config;
|
||||||
|
int ret = -ENODEV;
|
||||||
|
|
||||||
|
if (ep != VIDEO_EP_OUT) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Forward the message to the sensor device */
|
||||||
|
ret = video_get_caps(config->sensor_dev, ep, caps);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct video_driver_api video_stm32_dcmi_driver_api = {
|
||||||
|
.set_format = video_stm32_dcmi_set_fmt,
|
||||||
|
.get_format = video_stm32_dcmi_get_fmt,
|
||||||
|
.stream_start = video_stm32_dcmi_stream_start,
|
||||||
|
.stream_stop = video_stm32_dcmi_stream_stop,
|
||||||
|
.enqueue = video_stm32_dcmi_enqueue,
|
||||||
|
.dequeue = video_stm32_dcmi_dequeue,
|
||||||
|
.get_caps = video_stm32_dcmi_get_caps,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void video_stm32_dcmi_irq_config_func(const struct device *dev)
|
||||||
|
{
|
||||||
|
IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority),
|
||||||
|
stm32_dcmi_isr, DEVICE_DT_INST_GET(0), 0);
|
||||||
|
irq_enable(DT_INST_IRQN(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DCMI_DMA_CHANNEL_INIT(index, src_dev, dest_dev) \
|
||||||
|
.dma_dev = DEVICE_DT_GET(DT_INST_DMAS_CTLR_BY_IDX(index, 0)), \
|
||||||
|
.channel = DT_INST_DMAS_CELL_BY_IDX(index, 0, channel), \
|
||||||
|
.reg = (DMA_TypeDef *)DT_REG_ADDR( \
|
||||||
|
DT_PHANDLE_BY_IDX(DT_DRV_INST(0), dmas, 0)), \
|
||||||
|
.cfg = { \
|
||||||
|
.dma_slot = STM32_DMA_SLOT_BY_IDX(index, 0, slot), \
|
||||||
|
.channel_direction = STM32_DMA_CONFIG_DIRECTION( \
|
||||||
|
STM32_DMA_CHANNEL_CONFIG_BY_IDX(index, 0)), \
|
||||||
|
.source_data_size = STM32_DMA_CONFIG_##src_dev##_DATA_SIZE( \
|
||||||
|
STM32_DMA_CHANNEL_CONFIG_BY_IDX(index, 0)), \
|
||||||
|
.dest_data_size = STM32_DMA_CONFIG_##dest_dev##_DATA_SIZE( \
|
||||||
|
STM32_DMA_CHANNEL_CONFIG_BY_IDX(index, 0)), \
|
||||||
|
.source_burst_length = 1, /* SINGLE transfer */ \
|
||||||
|
.dest_burst_length = 1, /* SINGLE transfer */ \
|
||||||
|
.channel_priority = STM32_DMA_CONFIG_PRIORITY( \
|
||||||
|
STM32_DMA_CHANNEL_CONFIG_BY_IDX(index, 0)), \
|
||||||
|
.dma_callback = dmci_dma_callback, \
|
||||||
|
}, \
|
||||||
|
|
||||||
|
PINCTRL_DT_INST_DEFINE(0);
|
||||||
|
|
||||||
|
#define STM32_DCMI_GET_CAPTURE_RATE(capture_rate) \
|
||||||
|
((capture_rate) == 1 ? DCMI_CR_ALL_FRAME : \
|
||||||
|
(capture_rate) == 2 ? DCMI_CR_ALTERNATE_2_FRAME : \
|
||||||
|
(capture_rate) == 4 ? DCMI_CR_ALTERNATE_4_FRAME : \
|
||||||
|
DCMI_CR_ALL_FRAME)
|
||||||
|
|
||||||
|
#define STM32_DCMI_GET_BUS_WIDTH(bus_width) \
|
||||||
|
((bus_width) == 8 ? DCMI_EXTEND_DATA_8B : \
|
||||||
|
(bus_width) == 10 ? DCMI_EXTEND_DATA_10B : \
|
||||||
|
(bus_width) == 12 ? DCMI_EXTEND_DATA_12B : \
|
||||||
|
(bus_width) == 14 ? DCMI_EXTEND_DATA_14B : \
|
||||||
|
DCMI_EXTEND_DATA_8B)
|
||||||
|
|
||||||
|
#define DCMI_DMA_CHANNEL(id, src, dest) \
|
||||||
|
.dma = { \
|
||||||
|
COND_CODE_1(DT_INST_DMAS_HAS_IDX(id, 0), \
|
||||||
|
(DCMI_DMA_CHANNEL_INIT(id, src, dest)), \
|
||||||
|
(NULL)) \
|
||||||
|
},
|
||||||
|
|
||||||
|
static struct video_stm32_dcmi_data video_stm32_dcmi_data_0 = {
|
||||||
|
.hdcmi = {
|
||||||
|
.Instance = (DCMI_TypeDef *) DT_INST_REG_ADDR(0),
|
||||||
|
.Init = {
|
||||||
|
.SynchroMode = DCMI_SYNCHRO_HARDWARE,
|
||||||
|
.PCKPolarity = (DT_INST_PROP(0, pixelclk_active) ?
|
||||||
|
DCMI_PCKPOLARITY_RISING : DCMI_PCKPOLARITY_FALLING),
|
||||||
|
.HSPolarity = (DT_INST_PROP(0, hsync_active) ?
|
||||||
|
DCMI_HSPOLARITY_HIGH : DCMI_HSPOLARITY_LOW),
|
||||||
|
.VSPolarity = (DT_INST_PROP(0, vsync_active) ?
|
||||||
|
DCMI_VSPOLARITY_HIGH : DCMI_VSPOLARITY_LOW),
|
||||||
|
.CaptureRate = STM32_DCMI_GET_CAPTURE_RATE(
|
||||||
|
DT_INST_PROP(0, capture_rate)),
|
||||||
|
.ExtendedDataMode = STM32_DCMI_GET_BUS_WIDTH(
|
||||||
|
DT_INST_PROP(0, bus_width)),
|
||||||
|
.JPEGMode = DCMI_JPEG_DISABLE,
|
||||||
|
.ByteSelectMode = DCMI_BSM_ALL,
|
||||||
|
.ByteSelectStart = DCMI_OEBS_ODD,
|
||||||
|
.LineSelectMode = DCMI_LSM_ALL,
|
||||||
|
.LineSelectStart = DCMI_OELS_ODD,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct video_stm32_dcmi_config video_stm32_dcmi_config_0 = {
|
||||||
|
.pclken = {
|
||||||
|
.enr = DT_INST_CLOCKS_CELL(0, bits),
|
||||||
|
.bus = DT_INST_CLOCKS_CELL(0, bus)
|
||||||
|
},
|
||||||
|
.irq_config = video_stm32_dcmi_irq_config_func,
|
||||||
|
.pctrl = PINCTRL_DT_INST_DEV_CONFIG_GET(0),
|
||||||
|
.sensor_dev = DEVICE_DT_GET(DT_INST_PHANDLE(0, sensor)),
|
||||||
|
DCMI_DMA_CHANNEL(0, PERIPHERAL, MEMORY)
|
||||||
|
};
|
||||||
|
|
||||||
|
static int video_stm32_dcmi_init(const struct device *dev)
|
||||||
|
{
|
||||||
|
const struct video_stm32_dcmi_config *config = dev->config;
|
||||||
|
struct video_stm32_dcmi_data *data = dev->data;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/* Configure DT provided pins */
|
||||||
|
err = pinctrl_apply_state(config->pctrl, PINCTRL_STATE_DEFAULT);
|
||||||
|
if (err < 0) {
|
||||||
|
LOG_ERR("pinctrl setup failed. Error %d.", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize DMA peripheral */
|
||||||
|
err = stm32_dma_init(dev);
|
||||||
|
if (err < 0) {
|
||||||
|
LOG_ERR("DMA initialization failed.");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable DCMI clock */
|
||||||
|
err = stm32_dcmi_enable_clock(dev);
|
||||||
|
if (err < 0) {
|
||||||
|
LOG_ERR("Clock enabling failed.");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->dev = dev;
|
||||||
|
k_fifo_init(&data->fifo_in);
|
||||||
|
k_fifo_init(&data->fifo_out);
|
||||||
|
|
||||||
|
/* Run IRQ init */
|
||||||
|
config->irq_config(dev);
|
||||||
|
|
||||||
|
/* Initialize DCMI peripheral */
|
||||||
|
err = HAL_DCMI_Init(&data->hdcmi);
|
||||||
|
if (err != HAL_OK) {
|
||||||
|
LOG_ERR("DCMI initialization failed.");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
k_sleep(K_MSEC(100));
|
||||||
|
LOG_DBG("%s inited", dev->name);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEVICE_DT_INST_DEFINE(0, &video_stm32_dcmi_init,
|
||||||
|
NULL, &video_stm32_dcmi_data_0,
|
||||||
|
&video_stm32_dcmi_config_0,
|
||||||
|
POST_KERNEL, CONFIG_VIDEO_INIT_PRIORITY,
|
||||||
|
&video_stm32_dcmi_driver_api);
|
123
dts/bindings/video/st,stm32-dcmi.yaml
Normal file
123
dts/bindings/video/st,stm32-dcmi.yaml
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
#
|
||||||
|
# Copyright (c) 2024 Charles Dias <charlesdias.cd@outlook.com>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
|
description: |
|
||||||
|
STMicroelectronics STM32 Digital Camera Memory Interface (DCMI).
|
||||||
|
Example of node configuration at board level:
|
||||||
|
|
||||||
|
&dcmi {
|
||||||
|
status = "okay";
|
||||||
|
sensor = <&ov2640>;
|
||||||
|
pinctrl-0 = <&dcmi_hsync_pa4 &dcmi_pixclk_pa6 &dcmi_vsync_pb7
|
||||||
|
&dcmi_d0_pc6 &dcmi_d1_pc7 &dcmi_d2_pe0 &dcmi_d3_pe1
|
||||||
|
&dcmi_d4_pe4 &dcmi_d5_pd3 &dcmi_d6_pe5 &dcmi_d7_pe6>;
|
||||||
|
pinctrl-names = "default";
|
||||||
|
bus-width = <8>;
|
||||||
|
hsync-active = <0>;
|
||||||
|
vsync-active = <0>;
|
||||||
|
pixelclk-active = <1>;
|
||||||
|
capture-rate = <1>;
|
||||||
|
dmas = <&dma1 0 75 (STM32_DMA_PERIPH_TO_MEMORY | STM32_DMA_PERIPH_NO_INC |
|
||||||
|
STM32_DMA_MEM_INC | STM32_DMA_PERIPH_8BITS | STM32_DMA_MEM_32BITS |
|
||||||
|
STM32_DMA_PRIORITY_HIGH) STM32_DMA_FIFO_1_4>;
|
||||||
|
|
||||||
|
port {
|
||||||
|
dcmi_ep_in: endpoint {
|
||||||
|
remote-endpoint = <&ov2640_ep_out>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
compatible: "st,stm32-dcmi"
|
||||||
|
|
||||||
|
include: [base.yaml, pinctrl-device.yaml]
|
||||||
|
|
||||||
|
properties:
|
||||||
|
interrupts:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
sensor:
|
||||||
|
required: true
|
||||||
|
type: phandle
|
||||||
|
description: phandle of connected sensor device
|
||||||
|
|
||||||
|
bus-width:
|
||||||
|
type: int
|
||||||
|
required: true
|
||||||
|
enum:
|
||||||
|
- 8
|
||||||
|
- 10
|
||||||
|
- 12
|
||||||
|
- 14
|
||||||
|
default: 8
|
||||||
|
description: |
|
||||||
|
Number of data lines actively used, valid for the parallel busses.
|
||||||
|
|
||||||
|
hsync-active:
|
||||||
|
type: int
|
||||||
|
required: true
|
||||||
|
enum:
|
||||||
|
- 0
|
||||||
|
- 1
|
||||||
|
description: |
|
||||||
|
Polarity of horizontal synchronization (DCMI_HSYNC_Polarity).
|
||||||
|
0 Horizontal synchronization active Low.
|
||||||
|
1 Horizontal synchronization active High.
|
||||||
|
|
||||||
|
For example, if DCMI_HSYNC_Polarity is programmed active high:
|
||||||
|
When HSYNC is low, the data is valid.
|
||||||
|
When HSYNC is high, the data is not valid (horizontal blanking).
|
||||||
|
|
||||||
|
vsync-active:
|
||||||
|
type: int
|
||||||
|
required: true
|
||||||
|
enum:
|
||||||
|
- 0
|
||||||
|
- 1
|
||||||
|
description: |
|
||||||
|
Polarity of vertical synchronization (DCMI_VSYNC_Polarity).
|
||||||
|
0 Vertical synchronization active Low.
|
||||||
|
1 Vertical synchronization active High.
|
||||||
|
|
||||||
|
For example, if DCMI_VSYNC_Polarity is programmed active high:
|
||||||
|
When VSYNC is low, the data is valid.
|
||||||
|
When VSYNC is high, the data is not valid (vertical blanking).
|
||||||
|
|
||||||
|
pixelclk-active:
|
||||||
|
type: int
|
||||||
|
required: true
|
||||||
|
enum:
|
||||||
|
- 0
|
||||||
|
- 1
|
||||||
|
description: |
|
||||||
|
Polarity of pixel clock (DCMI_PIXCK_Polarity).
|
||||||
|
0 Pixel clock active on Falling edge.
|
||||||
|
1 Pixel clock active on Rising edge.
|
||||||
|
|
||||||
|
capture-rate:
|
||||||
|
type: int
|
||||||
|
enum:
|
||||||
|
- 1
|
||||||
|
- 2
|
||||||
|
- 4
|
||||||
|
default: 1
|
||||||
|
description: |
|
||||||
|
The DCMI can capture all frames or alternate frames. If it is not specified,
|
||||||
|
the default is all frames.
|
||||||
|
1 Capture all frames.
|
||||||
|
2 Capture alternate frames.
|
||||||
|
4 Capture one frame every 4 frames.
|
||||||
|
|
||||||
|
dmas:
|
||||||
|
required: true
|
||||||
|
description: |
|
||||||
|
phandle of DMA controller. The DMA controller should be compatible with
|
||||||
|
DMA channel specifier. Specifies a phandle reference to the dma controller,
|
||||||
|
the channel number, the slot number, channel configuration and finally features.
|
||||||
|
|
||||||
|
dmas = <&dma1 0 75 (STM32_DMA_PERIPH_TO_MEMORY | STM32_DMA_PERIPH_NO_INC |
|
||||||
|
STM32_DMA_MEM_INC | STM32_DMA_PERIPH_8BITS | STM32_DMA_MEM_32BITS |
|
||||||
|
STM32_DMA_PRIORITY_HIGH) STM32_DMA_FIFO_1_4>;
|
Loading…
Add table
Add a link
Reference in a new issue